# ====================
# DATA INTAKE
# Reading in raw CSV data
# ====================
loan_default <- read.csv("https://raw.githubusercontent.com/saltwatersoup/MyCV/refs/heads/main/Data%20Sets/Loan%20Default%20Data/BankLoanDefaultDataset.csv")
# Copying loan_default
loan_default_Mod01 <- loan_default
# List of starting variables
RawVars <- names(loan_default)
# ====================
# GENERATING CATEGORICAL VARIABLES FOR BINARY VARIABLES
# Generating Loan_Type
# Generating Default_YN
# loan_default_Mod01 is a copy of loan_default that has binary variables reflected as categorical variables
# ====================
# Creating variable Loan_Type which reflects Car_loan, Personal_loan, Home_loan, and Education_loan as one categorical variable.
loan_default_Mod01 <- loan_default_Mod01 %>%
mutate(
Loan_Type = case_when(
Car_loan == 1 ~ "Car",
Personal_loan == 1 ~ "Personal",
Home_loan == 1 ~ "Home",
Education_loan == 1 ~ "Education",
TRUE ~ "Other"
)
)
# Creating variable Default_YN which reflects Default as a categorical variable.
loan_default_Mod01 <- loan_default_Mod01 %>%
mutate(
Default_YN = case_when(
Default == 0 ~ "No Default",
Default == 1 ~ "Defaulted",
TRUE ~ NA
)
)
# ====================
# CAPITALIZING ALL CATEGORICAL VARIABLES
# By default, some of the character responses are lowercase
# loan_default_Mod02 is a copy of loan_default_Mod01 that has character entries with uppercase first letters
# ====================
# Copying loan_default_Mod01
loan_default_Mod02 <- loan_default_Mod01
# Selecting character variables and making their first letters uppercase
loan_default_Mod02[sapply(loan_default_Mod02, typeof) == "character"] <-
loan_default_Mod02[sapply(loan_default_Mod02, typeof) == "character"] %>%
sapply(
function(x) {
substr(x, 1, 1) <- toupper(substr(x, 1, 1))
x
}
)
# ====================
# DATA MODIFICATION
# Adding missing values
# loan_default_Mod03 is a copy of loan_default_Mod02 with missing values
# loan_default_MissLoc shows the locations of all missing values via TRUE
# loan_default_True shows all observations from loan_default_Mod02 that were given missing values in loan_default_Mod03
# ====================
# Copying loan_default_Mod02
loan_default_Mod03 <- loan_default_Mod02
# Creating random observation IDs and replacing the corresponding observations with missing
# loan_default_Mod03$Checking_Amount[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Term[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Credit_score[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod03$Gender[sample(1:1000, 68, replace = FALSE)] <- NA
loan_default_Mod03$Marital_status[sample(1:1000, 87, replace = FALSE)] <- NA
# loan_default_Mod03$Car_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Personal_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Home_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Education_loan[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod03$Emp_status[sample(1:1000, 136, replace = FALSE)] <- NA
# loan_default_Mod03$Amount[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod03$Saving_amoun[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod03$Emp_duration[sample(1:1000, 201, replace = FALSE)] <- NA
loan_default_Mod03$Age[sample(1:1000, 159, replace = FALSE)] <- NA
# loan_default_Mod03$No_of_credit_account[sample(1:1000, 100, replace = FALSE)] <- NA
# Showing the locations of all missing values via TRUE
loan_default_MissLoc <- sapply(loan_default_Mod03, is.na)
# Showing all rows of complete data that were given missing values
loan_default_True <- loan_default_Mod02[(rowSums(sapply(loan_default_Mod03, is.na)) > 0), ]
# ====================
# CATEGORIZING VARIABLES
# Categorizing variables in loan_default_Mod03 into binary, numeric, and categorical
# ====================
# Identifying variable types
loan_default_VarType <-
loan_default_Mod03 %>%
sapply(typeof)
# Identifying binary variables
# Variables that contain 0, 1, or NA
loan_default_VarType[
apply(
loan_default_Mod03,
2,
function(x){all(match(x, c(0, 1, NA), nomatch = FALSE))}
)
] <- "Binary"
# Identifying numeric variables
# Variables that are numbers but not binary
loan_default_VarType[loan_default_VarType == "integer"] <- "Numeric"
# Identifying categorical variables
# Variables that are characters
loan_default_VarType[loan_default_VarType == "character"] <- "Categorical"
Introduction
With real-world data, it is common to encounter missing values
resulting from entry error, non-response, hardware failure, etc. In some
cases, ignoring missing values might not make a significant impact in a
study’s conclusions, but in other cases, ignoring missing values can
lead to significant deviations from the truth or even false
conclusions.
This program serves as an exercise in addressing missing data by
using BankLoanDefaultDataset.csv as its base. Because
BankLoanDefaultDataset.csv has no missing values to start
with, some values were replaced with NA to simulate an
incomplete dataset.
The following plots show which variables contain missing values and
how many. Hovering over each bar gives further details.
# ====================
# PLOTTING MISSING VALUES
# ====================
# Generating data frame of missing values per variable
MissDatCounts <- data.frame(
Variables = names(loan_default_Mod03),
VarType = loan_default_VarType,
Missing = colSums(is.na(loan_default_Mod03))
)
# Generating interactive plot using plotly
Plot_MissingVals <-
# Taking a subset of MissDatCounts, so only entries with > 0 missing values will be displayed
subset(MissDatCounts, Missing > 0) %>%
# Passing the subset to plot_ly
plot_ly(
x = ~Variables,
y = ~Missing,
split = ~VarType,
hovertemplate = ~paste0(
"<b>Count</b>: ", Missing, " of 1000<br>",
"<b>\U0025 Missing</b>: ", round(Missing / 1000 * 100, digits = 3), "\U0025"
)
) %>%
layout(
title = list(
text = "Missing Values per Variable"
),
xaxis = list(
title = "Variables with Missing Values",
categoryorder = "trace"
),
yaxis = list(
title = "Number of Missing Values"
),
legend = list(
title = list(text = "<b> Variable Type </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
Plot_MissingVals
# ====================
# PLOTTING CATEGORICAL VARIABLES WITH MISSING VALUES
# ====================
# Subsetting loan_default_Mod03 into the variables that are categorical and have missing data
CatVars_Miss <-
loan_default_Mod03[
(loan_default_VarType == "Categorical") & (colSums(is.na(loan_default_Mod03)) > 0)
]
Fig_Data <- c()
for(i in 1:ncol(CatVars_Miss)) {
Fig_Data[[i]] <- CatVars_Miss %>%
count(.data[[names(CatVars_Miss)[i]]])
}
# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in CatVars_Miss
for(i in 1:length(Fig_Data)){
NumFig[[i]] <- plot_ly()
for(j in 1:nrow(Fig_Data[[i]])) {
NumFig[[i]] <- NumFig[[i]] %>%
add_trace(
x = names(CatVars_Miss)[i],
y = Fig_Data[[i]][j,2],
type = "bar",
name = Fig_Data[[i]][j,1],
legendgroup = names(CatVars_Miss)[i],
legendgrouptitle = list(text = names(CatVars_Miss)[i]),
hovertemplate = paste0(
"<b>Count</b>: ", Fig_Data[[i]][j,2], " of 1000<br>",
"<b>Percentage</b>: ", round(Fig_Data[[i]][j,2] / 1000 * 100, digits = 3), "\U0025"
)
) %>%
layout(
yaxis = list(range = c(0, 700))
)
}
}
Fig_CatVars_Miss <-
subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], nrows = 1, margin = 0.05) %>%
layout(
title = "Categorical Variables with Missing Values",
legend = list(
title = list(text = "<b> Categorical <br> Responses </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
),
yaxis = list(
title = "Number of Entries"
)
)
Fig_CatVars_Miss
# ====================
# PLOTTING NUMERIC VARIABLES WITH MISSING VALUES
# ====================
# Subsetting loan_default_Mod03 into the variables that are categorical and have missing data
NumVars_Miss <-
loan_default_Mod03[
(loan_default_VarType == "Numeric") & (colSums(is.na(loan_default_Mod03)) > 0)
]
NumVars_Miss_Mod01 <- NumVars_Miss %>%
sapply(is.na) %>%
ifelse(NA, "Non-missing") %>%
data.frame()
Fig_Data <- c()
for(i in 1:ncol(NumVars_Miss_Mod01)) {
Fig_Data[[i]] <- NumVars_Miss_Mod01 %>%
count(.data[[names(NumVars_Miss_Mod01)[i]]])
}
# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in NumVars_Miss_Mod01
for(i in 1:length(Fig_Data)){
NumFig[[i]] <- plot_ly()
for(j in 1:nrow(Fig_Data[[i]])) {
NumFig[[i]] <- NumFig[[i]] %>%
add_trace(
x = names(NumVars_Miss_Mod01)[i],
y = Fig_Data[[i]][j,2],
type = "bar",
name = Fig_Data[[i]][j,1],
legendgroup = names(NumVars_Miss_Mod01)[i],
legendgrouptitle = list(text = names(NumVars_Miss_Mod01)[i]),
hovertemplate = paste0(
"<b>Count</b>: ", Fig_Data[[i]][j,2], " of 1000<br>",
"<b>Percentage</b>: ", round(Fig_Data[[i]][j,2] / 1000 * 100, digits = 3), "\U0025"
)
) %>%
layout(
yaxis = list(range = c(0, 850))
)
}
}
Fig_NumVars_Miss <-
subplot(NumFig[[1]], NumFig[[2]], nrows = 1, margin = 0.05) %>%
layout(
title = "Numerical Variables with Missing Values",
legend = list(
title = list(text = "<b> Responses </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
),
yaxis = list(
title = "Number of Entries"
)
)
Fig_NumVars_Miss
Imputation
Imputation is the process of replacing missing data with substitute
values. Ideally, we want to impute values as close as possible to the
true value that is missing. To that end, we must try to predict what the
missing value would have been.
We will explore the following methods of imputation:
- k-Nearest Neighbors (k-NN) for categorical variables with
missing values
- Random regression imputation for numerical variables with missing
values
- Multiple Imputation by Chained Equations (MICE) for all missing
values
Feature
Engineering
Feature engineering is the process of selecting, transforming, and
creating feature variables to improve the performance of predictive
models.
For the purpose of feature selection, we use a wrapper method, which
consists of using a predictive model to evaluate the performance of
different combinations of features then selecting the highest performing
set of features. The caret package enables us to use
recursive feature elimination (RFE) to output the list of features that
give us the best performance. The following output gives us the best set
of feature variables to predict the label variable,
Default.
# ====================
# FEATURE SELECTION
# Dr. Peng's code was used as a reference
# ====================
results <- rfe(
loan_default[, -1],
loan_default$Default,
sizes = c(1:5),
rfeControl = rfeControl(functions = rfFuncs, method = "cv", number = 10)
)
predictors(results)
[1] "Age" "Credit_score" "Checking_amount" "Saving_amount"
[5] "Education_loan" "Term" "Personal_loan" "Home_loan"
[9] "Car_loan" "Amount" "Emp_status" "Emp_duration"
[13] "Marital_status" "No_of_credit_acc" "Gender"
As RFE has returned all of our original feature variables, we proceed
without eliminating any features from further analysis.
Visualizing all numerical non-binary variables, we can see that:
- The narrow numerical range and steep drops of
No_of_credit_acc make it a prime candidate for
binning.
- The remaining numerical variables are candidates for standardization
to better support future modeling.
# ====================
# PLOTTING ALL NUMERICAL NON-BINARY VARIABLES
# ====================
# Selecting only numeric variables
NumVars <- select(loan_default_Mod03, where(is.numeric))
# Selecting eliminating any binary variables
NumVars <- NumVars[!apply(NumVars, 2, function(x){all(match(x, c(0, 1, NA), nomatch = FALSE))})]
# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in NumVars
for(i in 1:length(names(NumVars))){
NumFig[[i]] <- plot_ly(
x = NumVars[[i]],
# y = "",
type = "histogram",
name = colnames(NumVars)[i]
)
}
# Generating a plot that contains 8 subplots (one for each variable in NumVars) across 4 rows
Plot_NumVars <-
subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], NumFig[[4]], NumFig[[5]], NumFig[[6]], NumFig[[7]], NumFig[[8]], nrows = 4, margin = 0.05) %>%
layout(
title = "Distributions of All Numerical Non-binary Variables",
legend = list(
title = list(text = "<b> Variable </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
Plot_NumVars
Binning No_of_credit_acc according to the sharp drops in
the histogram results in the creation of new variable
No_of_credit_acc_Bins with the following breakdown:
# ====================
# BINNING No_of_credit_acc
# ====================
# Transforming No_of_credit_acc into ordinal categorical variables
loan_default_Mod04 <- loan_default_Mod03 %>%
mutate(
No_of_credit_acc_Bins = cut(
No_of_credit_acc,
breaks = c(1, 2, 5, 9),
include.lowest = TRUE
)
)
table(loan_default_Mod04$No_of_credit_acc_Bins)
[1,2] (2,5] (5,9]
633 333 34
Standardization of the remaining seven numerical variables results in
the following distribution:
# ====================
# FEATURE STANDARDIZATION
# ====================
# Normalization function from Dr. Peng
standardize <- function(x) {
return((x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE))
}
# Normalizing numeric variables
loan_default_Mod04$Checking_amount_Stand <-
standardize(loan_default_Mod04$Checking_amount)
loan_default_Mod04$Term_Stand <-
standardize(loan_default_Mod04$Term)
loan_default_Mod04$Credit_score_Stand <-
standardize(loan_default_Mod04$Credit_score)
loan_default_Mod04$Amount_Stand <-
standardize(loan_default_Mod04$Amount)
loan_default_Mod04$Saving_amount_Stand <-
standardize(loan_default_Mod04$Saving_amount)
loan_default_Mod04$Emp_duration_Stand <-
standardize(loan_default_Mod04$Emp_duration)
loan_default_Mod04$Age_Stand <-
standardize(loan_default_Mod04$Age)
# List of standardized variables
StandVars <- c("Checking_amount_Stand", "Term_Stand", "Credit_score_Stand", "Amount_Stand", "Saving_amount_Stand", "Emp_duration_Stand", "Age_Stand")
# Numeric variable data for de-standardization
DeStandVarData <-
data.frame(
Variable = c("Checking_amount", "Term", "Credit_score", "Amount", "Saving_amount", "Emp_duration", "Age"),
Mean = c(
mean(loan_default_Mod04$Checking_amount, na.rm = TRUE),
mean(loan_default_Mod04$Term, na.rm = TRUE),
mean(loan_default_Mod04$Credit_score, na.rm = TRUE),
mean(loan_default_Mod04$Amount, na.rm = TRUE),
mean(loan_default_Mod04$Saving_amount, na.rm = TRUE),
mean(loan_default_Mod04$Emp_duration, na.rm = TRUE),
mean(loan_default_Mod04$Age, na.rm = TRUE)
),
SD = c(
sd(loan_default_Mod04$Checking_amount, na.rm = TRUE),
sd(loan_default_Mod04$Term, na.rm = TRUE),
sd(loan_default_Mod04$Credit_score, na.rm = TRUE),
sd(loan_default_Mod04$Amount, na.rm = TRUE),
sd(loan_default_Mod04$Saving_amount, na.rm = TRUE),
sd(loan_default_Mod04$Emp_duration, na.rm = TRUE),
sd(loan_default_Mod04$Age, na.rm = TRUE)
)
)
# ====================
# PLOTTING ALL STANDARDIZED NUMERICAL VARIABLES
# ====================
# Selecting only standardized variables
StandNumVars <- loan_default_Mod04[StandVars]
# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in StandNumVars
for(i in 1:length(names(StandNumVars))){
NumFig[[i]] <- plot_ly(
x = StandNumVars[[i]],
# y = "",
type = "histogram",
name = colnames(StandNumVars)[i]
)
}
# Generating a plot that contains 8 subplots (one for each variable in StandNumVars) across 4 rows
Plot_StandNumVars <-
subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], NumFig[[4]], NumFig[[5]], NumFig[[6]], NumFig[[7]], nrows = 4, margin = 0.05) %>%
layout(
title = "Distributions of All Standardized Numerical Variables",
legend = list(
title = list(text = "<b> Variable </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
Plot_StandNumVars
Replacement Imputation
for Categorical Features
The data contains missing values in the variables
Gender, Marital_status, and
Emp_status, and these missing values must be addressed
before we can proceed with modeling. To that end, we use k-NN
to impute the missing values. The results of this imputation can be seen
in the following plot:
# ====================
# IMPUTING CATEGORICAL VALUES WITH kNN
# CatImpute is loan_default_Mod04 with imputed categorical values
# ====================
CatImpute <- kNN(
loan_default_Mod04[RawVars],
variable = c("Gender", "Marital_status", "Emp_status"),
k = 5
)
# ====================
# PLOTTING CATEGORICAL VARIABLES WITH IMPUTED VALUES
# ====================
# Subsetting loan_default_Mod04 into the variables that are categorical and have missing data
CatVars_Imp <- CatImpute[
c("Gender", "Marital_status", "Emp_status",
"Gender_imp", "Marital_status_imp", "Emp_status_imp")
]
CatVars_Imp <- CatVars_Imp %>%
mutate(
Gender = case_when(
Gender_imp == TRUE ~ paste0(Gender, " (Imputed)"),
TRUE ~ Gender
),
Marital_status = case_when(
Marital_status_imp == TRUE ~ paste0(Marital_status, " (Imputed)"),
TRUE ~ Marital_status
),
Emp_status = case_when(
Emp_status_imp == TRUE ~ paste0(Emp_status, " (Imputed)"),
TRUE ~ Emp_status
),
)
Fig_Data <- c()
for(i in 1:ncol(CatVars_Imp)) {
Fig_Data[[i]] <- CatVars_Imp %>%
count(.data[[names(CatVars_Imp)[i]]])
}
# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in CatVars_Imp
for(i in 1:3){
NumFig[[i]] <- plot_ly()
for(j in 1:nrow(Fig_Data[[i]])) {
NumFig[[i]] <- NumFig[[i]] %>%
add_trace(
x = names(CatVars_Imp)[i],
y = Fig_Data[[i]][j,2],
type = "bar",
name = Fig_Data[[i]][j,1],
legendgroup = names(CatVars_Imp)[i],
legendgrouptitle = list(text = names(CatVars_Imp)[i]),
hovertemplate = paste0(
"<b>Count</b>: ", Fig_Data[[i]][j,2], " of 1000<br>",
"<b>Percentage</b>: ", round(Fig_Data[[i]][j,2] / 1000 * 100, digits = 3), "\U0025"
)
)
}
}
Fig_CatVars_Imp <-
subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], nrows = 1, margin = 0.05, shareY = TRUE) %>%
layout(
title = "Categorical Variables with Missing Values",
legend = list(
title = list(text = "<b> Categorical <br> Responses </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
),
yaxis = list(
title = "Number of Entries"
)
)
Fig_CatVars_Imp
The results of this method of imputation roughly appear to reflect
the distribution of the non-missing data. This would make sense if the
data is missing at random.
Random Regression-based
Imputation for Numerical Features
The data contains missing values in the variables
Emp_duration and Age, and these missing values
must be addressed before we can proceed with modeling. To that end, we
use random regression to impute the missing values.
We selected the variable to reference by looking at the correlation
plot between numerical variables. By having the highest correlations,
No_of_credit_acc was chosen for Emp_duration
and Saving_amount was chosen for Age.
# ====================
# PLOTTING CORRELATION BETWEEN NUMERICAL VARIABLES
# ====================
corrplot(
cor(
loan_default_Mod04[
c(
# "Default",
"Checking_amount",
"Term",
"Credit_score",
# "Gender",
# "Marital_status",
# "Car_loan",
# "Personal_loan",
# "Home_loan",
# "Education_loan",
# "Emp_status",
"Amount",
"Saving_amount",
"Emp_duration",
"Age",
"No_of_credit_acc"
)
],
use = "complete.obs"
),
type = "lower",
tl.srt = 35,
addCoef.col = 'grey50'
)

The following is the result of the imputation:
# ====================
# IMPUTING NUMERICAL VALUES WITH LINEAR REGRESSION
# NumImpute is loan_default_Mod04 with imputed numerical values
# Dr. Peng's code was used as a reference
# ====================
# Generating linear imputation model for Emp_duration
NumImpute_Model_Emp_duration <- lm(
Emp_duration ~
# Default +
# Checking_amount +
# Term +
# Credit_score +
# # Gender +
# # Marital_status +
# Car_loan +
# Personal_loan +
# Home_loan +
# Education_loan +
# # Emp_status +
# Amount +
# Saving_amount +
# Emp_duration +
# Age +
No_of_credit_acc,
data = loan_default_Mod04
)
# Generating linear imputation model for Age
NumImpute_Model_Age <- lm(
Age ~
# Default +
# Checking_amount +
# Term +
# Credit_score +
# Gender +
# Marital_status +
# Car_loan +
# Personal_loan +
# Home_loan +
# Education_loan +
# Emp_status +
# Amount +
Saving_amount,
# Emp_duration +
# Age +
# No_of_credit_acc,
data = loan_default_Mod04
)
# Copying loan_default_Mod04
NumImpute <- loan_default_Mod04
# Imputing Emp_duration with random regression
NumImpute$Emp_duration[is.na(loan_default_Mod04$Emp_duration)] <-
# Predicted value
predict(
NumImpute_Model_Emp_duration,
loan_default_Mod04[is.na(loan_default_Mod04$Emp_duration),],
type = "response"
) +
# Added randomness via residual
sample(
resid(NumImpute_Model_Emp_duration),
sum(is.na(loan_default_Mod04$Emp_duration)),
replace = TRUE
)
# Imputing Age with random regression
NumImpute$Age[is.na(loan_default_Mod04$Age)] <-
# Predicted value
predict(
NumImpute_Model_Age,
loan_default_Mod04[is.na(loan_default_Mod04$Age),],
type = "response"
) +
# Added randomness via residual
sample(
resid(NumImpute_Model_Age),
sum(is.na(loan_default_Mod04$Age)),
replace = TRUE
)
# ====================
# PLOTTING Emp_duration AND Age WITH IMPUTED VALUES
# ====================
Plot_NumImp_Parts <- NULL
# Generating subplot
Plot_NumImp_Parts[[1]] <-
plot_ly() %>%
add_trace(
data = loan_default_Mod04,
x = ~Emp_duration,
name = "Non-missing",
legendgroup = "Emp_duration",
legendgrouptitle = list(text = "Emp_duration")
) %>%
add_trace(
x = NumImpute$Emp_duration[is.na(loan_default_Mod04$Emp_duration)],
name = "Imputed: Emp_Duration",
legendgroup = "Emp_duration",
legendgrouptitle = list(text = "Emp_duration")
)
# Generating subplot
Plot_NumImp_Parts[[2]] <-
plot_ly() %>%
add_trace(
data = loan_default_Mod04,
x = ~Age,
name = "Non-missing",
legendgroup = "Age",
legendgrouptitle = list(text = "Age")
) %>%
add_trace(
x = NumImpute$Age[is.na(loan_default_Mod04$Age)],
name = "Imputed: Age",
legendgroup = "Age",
legendgrouptitle = list(text = "Age")
)
# Generating full plot
Plot_NumImp <-
subplot(Plot_NumImp_Parts[[1]], Plot_NumImp_Parts[[2]], nrows = 1, margin = 0.05, shareY = TRUE) %>%
layout(
title = "Employment Duration and Age with Imputation",
barmode = "stack",
bargap = 0.1,
legend = list(
title = list(text = "<b> Responses </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
Plot_NumImp
It is worth noting that this method has the possibility of imputing
values outside of the possible range of the data. Therefore, it is worth
considering an alternative method of imputation.
Multiple Imputation by
Chained Equations (MICE)
Multiple Imputation by Chained Equations (MICE) is a method of
imputing that iterates through each variable containing missing data and
refining estimates. Therefore, this method allows us to impute
Gender, Marital_status,
Emp_status, Emp_duration, and Age
at the same time.
The results of MICE can be seen in the plots below:
# ====================
# PERFORMING MULTIPLE IMPUTATION
# ====================
loan_default_MultImp <-
complete(
mice(
loan_default_Mod04[RawVars],
method = c(
# Default
NA,
# Checking_amount
NA,
# Term
NA,
# Credit_score
NA,
# Gender
"logreg",
# Marital_status
"logreg",
# Car_loan
NA,
# Personal_loan
NA,
# Home_loan
NA,
# Education_loan
NA,
# Emp_status
"logreg",
# Amount
NA,
# Saving_amount
NA,
# Emp_duration
"pmm",
# Age
"pmm",
# No_of_credit_acc
NA
),
maxit = 5,
print = F,
seed = 123
)
)
# ====================
# PLOTTING CATEGORICAL VARIABLES WITH MICE
# ====================
MICE_CatImp_Gender_Combo <- data.frame(
Cat = c("Female", "Female (Imputed)", "Male", "Male (Imputed)", "NA (Imputed)"),
Val = c(
table(loan_default_Mod04$Gender)["Female"],
table(loan_default_MultImp$Gender[is.na(loan_default_Mod04$Gender)], useNA = "always")["Female"],
table(loan_default_Mod04$Gender)["Male"],
table(loan_default_MultImp$Gender[is.na(loan_default_Mod04$Gender)], useNA = "always")["Male"],
table(loan_default_MultImp$Gender[is.na(loan_default_Mod04$Gender)], useNA = "always")[3]
)
)
MICE_CatImp_Gender_Combo$Cat <- MICE_CatImp_Gender_Combo$Cat %>%
factor(levels = MICE_CatImp_Gender_Combo[["Cat"]])
MICE_CatImp_Marital_status_Combo <- data.frame(
Cat = c("Married", "Married (Imputed)", "Single", "Single (Imputed)", "NA (Imputed)"),
Val = c(
table(loan_default_Mod04$Marital_status)["Married"],
table(loan_default_MultImp$Marital_status[is.na(loan_default_Mod04$Marital_status)], useNA = "always")["Married"],
table(loan_default_Mod04$Marital_status)["Single"],
table(loan_default_MultImp$Marital_status[is.na(loan_default_Mod04$Marital_status)], useNA = "always")["Single"],
table(loan_default_MultImp$Marital_status[is.na(loan_default_Mod04$Marital_status)], useNA = "always")[3]
)
)
MICE_CatImp_Marital_status_Combo$Cat <- MICE_CatImp_Marital_status_Combo$Cat %>%
factor(levels = MICE_CatImp_Marital_status_Combo[["Cat"]])
MICE_CatImp_Emp_status_Combo <- data.frame(
Cat = c("Employed", "Employed (Imputed)", "Unemployed", "Unemployed (Imputed)", "NA (Imputed)"),
Val = c(
table(loan_default_Mod04$Emp_status)["Employed"],
table(loan_default_MultImp$Emp_status[is.na(loan_default_Mod04$Emp_status)], useNA = "always")["Employed"],
table(loan_default_Mod04$Emp_status)["Unemployed"],
table(loan_default_MultImp$Emp_status[is.na(loan_default_Mod04$Emp_status)], useNA = "always")["Unemployed"],
table(loan_default_MultImp$Emp_status[is.na(loan_default_Mod04$Emp_status)], useNA = "always")[3]
)
)
MICE_CatImp_Emp_status_Combo$Cat <- MICE_CatImp_Emp_status_Combo$Cat %>%
factor(levels = MICE_CatImp_Emp_status_Combo[["Cat"]])
# ========================
Plot_MultImp_Cat_Parts <- NULL
# Generating subplot
Plot_MultImp_Cat_Parts[[1]] <-
plot_ly() %>%
add_trace(
data = MICE_CatImp_Gender_Combo,
x = "Gender",
y = ~Val,
name = ~Cat,
type = "bar",
legendgroup = "Gender",
legendgrouptitle = list(text = "Gender")
) %>%
layout(barmode = "group")
# Generating subplot
Plot_MultImp_Cat_Parts[[2]] <-
plot_ly() %>%
add_trace(
data = MICE_CatImp_Marital_status_Combo,
x = "Marital_status",
y = ~Val,
name = ~Cat,
type = "bar",
legendgroup = "Marital_status",
legendgrouptitle = list(text = "Marital_status")
) %>%
layout(barmode = "group")
# Generating subplot
Plot_MultImp_Cat_Parts[[3]] <-
plot_ly() %>%
add_trace(
data = MICE_CatImp_Emp_status_Combo,
x = "Emp_status",
y = ~Val,
name = ~Cat,
type = "bar",
legendgroup = "Emp_status",
legendgrouptitle = list(text = "Emp_status")
) %>%
layout(barmode = "group")
# Generating full plot
Plot_MultImp_Cat <-
subplot(Plot_MultImp_Cat_Parts[[1]], Plot_MultImp_Cat_Parts[[2]], Plot_MultImp_Cat_Parts[[3]], nrows = 1, margin = 0.05, shareY = TRUE) %>%
layout(
title = "Categorical MICE Results",
bargap = 0.1,
yaxis = list(
title = ""
),
legend = list(
title = list(text = "<b> Responses </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
Plot_MultImp_Cat
For the categorical variables, MICE appears to have resulted in
similar imputation as with k-NN but has returned missing
values. Therefore, MICE imputation may not be the optimal way to impute
categorical variables.
# ====================
# PLOTTING NUMERICAL VARIABLES WITH MICE
# ====================
Plot_MultImp_Num_Parts <- NULL
# Generating subplot
Plot_MultImp_Num_Parts[[1]] <-
plot_ly() %>%
add_trace(
data = loan_default_Mod04,
x = ~Emp_duration,
name = "Non-missing",
legendgroup = "Emp_duration",
legendgrouptitle = list(text = "Emp_duration")
) %>%
add_trace(
x = loan_default_MultImp$Emp_duration[is.na(loan_default_Mod04$Emp_duration)],
name = "Imputed: Emp_Duration",
legendgroup = "Emp_duration",
legendgrouptitle = list(text = "Emp_duration")
) %>%
layout(barmode = "stack")
# Generating subplot
Plot_MultImp_Num_Parts[[2]] <-
plot_ly() %>%
add_trace(
data = loan_default_Mod04,
x = ~Age,
name = "Non-missing",
legendgroup = "Age",
legendgrouptitle = list(text = "Age")
) %>%
add_trace(
x = loan_default_MultImp$Age[is.na(loan_default_Mod04$Age)],
name = "Imputed: Age",
legendgroup = "Age",
legendgrouptitle = list(text = "Age")
) %>%
layout(barmode = "stack")
# Generating full plot
Plot_MultImp_Num <-
subplot(Plot_MultImp_Num_Parts[[1]], Plot_MultImp_Num_Parts[[2]], nrows = 1, margin = 0.05, shareY = TRUE) %>%
layout(
title = "Numerical MICE Results",
bargap = 0.1,
legend = list(
title = list(text = "<b> Responses </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
Plot_MultImp_Num
For the numerical variables, MICE appears to have resulted in similar
imputation as with random regression but does not generate values
outside of the possible range. This improvement makes MICE preferable to
random regression imputation.
LS0tDQp0aXRsZTogIkxvYW4gRGVmYXVsdCBEYXRhIFtJbXB1dGF0aW9uIGFuZCBGZWF0dXJlIEVuZ2luZWVyaW5nXSINCmF1dGhvcjogIktvamkgU2hpbW9tdXJhIg0KZGF0ZTogIiAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogbmF2eTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgRklMRSBTRVRVUA0KIyBDaGVja2luZyBhbmQgaW5zdGFsbGluZyBuZWNlc3NhcnkgbGlicmFyaWVzDQojIFNwZWNpZnlpbmcgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBDb25kaXRpb25hbGx5IGluc3RhbGxpbmcgcGFja2FnZXMNCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCmlmICghcmVxdWlyZSgicGFsbWVycGVuZ3VpbnMiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFsbWVycGVuZ3VpbnMiKQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCn0NCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQpsaWJyYXJ5KHBsb3RseSkNCn0NCmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpDQpsaWJyYXJ5KEdHYWxseSkNCn0NCmlmICghcmVxdWlyZSgibmFuaWFyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm5hbmlhciIpDQpsaWJyYXJ5KG5hbmlhcikNCn0NCmlmICghcmVxdWlyZSgicG9vbCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwb29sIikNCmxpYnJhcnkocG9vbCkNCn0NCmlmICghcmVxdWlyZSgiREJJIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIkRCSSIpDQpsaWJyYXJ5KERCSSkNCn0NCmlmICghcmVxdWlyZSgiUk15U1FMIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIlJNeVNRTCIpDQpsaWJyYXJ5KFJNeVNRTCkNCn0NCmlmICghcmVxdWlyZSgicmFuZG9tRm9yZXN0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCn0NCmlmICghcmVxdWlyZSgiZ2dpcmFwaCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ2lyYXBoIikNCmxpYnJhcnkoZ2dpcmFwaCkNCn0NCmlmICghcmVxdWlyZSgiaGlnaGNoYXJ0ZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiaGlnaGNoYXJ0ZXIiKQ0KbGlicmFyeShoaWdoY2hhcnRlcikNCn0NCmlmICghcmVxdWlyZSgiYnJvb20iKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiYnJvb20iKQ0KbGlicmFyeShicm9vbSkNCn0NCg0KaWYgKCFyZXF1aXJlKCJWSU0iKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiVklNIikNCmxpYnJhcnkoVklNKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjb3JycGxvdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjb3JycGxvdCIpDQpsaWJyYXJ5KGNvcnJwbG90KQ0KfQ0KaWYgKCFyZXF1aXJlKCJtaWNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiKQ0KbGlicmFyeShtaWNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpsaWJyYXJ5KGNhcmV0KQ0KfQ0KDQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICAjIGluY2x1ZGUgY29kZSBjaHVuayBpbiB0aGUgb3V0cHV0IGZpbGUNCiAgZWNobyA9IFRSVUUsDQogICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLCB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgd2FybmluZyA9IEZBTFNFLCANCiAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0IGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgcmVzdWx0cyA9IFRSVUUsIA0KICBtZXNzYWdlID0gRkFMU0UsDQogIGNvbW1lbnQgPSBOQQ0KKSAgDQpgYGANCg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgREFUQSBJTlRBS0UNCiMgUmVhZGluZyBpbiByYXcgQ1NWIGRhdGENCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KbG9hbl9kZWZhdWx0IDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc2FsdHdhdGVyc291cC9NeUNWL3JlZnMvaGVhZHMvbWFpbi9EYXRhJTIwU2V0cy9Mb2FuJTIwRGVmYXVsdCUyMERhdGEvQmFua0xvYW5EZWZhdWx0RGF0YXNldC5jc3YiKQ0KDQojIENvcHlpbmcgbG9hbl9kZWZhdWx0DQpsb2FuX2RlZmF1bHRfTW9kMDEgPC0gbG9hbl9kZWZhdWx0DQoNCiMgTGlzdCBvZiBzdGFydGluZyB2YXJpYWJsZXMNClJhd1ZhcnMgPC0gbmFtZXMobG9hbl9kZWZhdWx0KQ0KYGBgDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBHRU5FUkFUSU5HIENBVEVHT1JJQ0FMIFZBUklBQkxFUyBGT1IgQklOQVJZIFZBUklBQkxFUw0KIyBHZW5lcmF0aW5nIExvYW5fVHlwZQ0KIyBHZW5lcmF0aW5nIERlZmF1bHRfWU4NCiMgbG9hbl9kZWZhdWx0X01vZDAxIGlzIGEgY29weSBvZiBsb2FuX2RlZmF1bHQgdGhhdCBoYXMgYmluYXJ5IHZhcmlhYmxlcyByZWZsZWN0ZWQgYXMgY2F0ZWdvcmljYWwgdmFyaWFibGVzDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgQ3JlYXRpbmcgdmFyaWFibGUgTG9hbl9UeXBlIHdoaWNoIHJlZmxlY3RzIENhcl9sb2FuLCBQZXJzb25hbF9sb2FuLCBIb21lX2xvYW4sIGFuZCBFZHVjYXRpb25fbG9hbiBhcyBvbmUgY2F0ZWdvcmljYWwgdmFyaWFibGUuDQpsb2FuX2RlZmF1bHRfTW9kMDEgPC0gbG9hbl9kZWZhdWx0X01vZDAxICU+JSANCiAgbXV0YXRlKA0KICAgIExvYW5fVHlwZSA9IGNhc2Vfd2hlbigNCiAgICAgIENhcl9sb2FuID09IDEgfiAiQ2FyIiwNCiAgICAgIFBlcnNvbmFsX2xvYW4gPT0gMSB+ICJQZXJzb25hbCIsDQogICAgICBIb21lX2xvYW4JPT0gMSB+ICJIb21lIiwNCiAgICAgIEVkdWNhdGlvbl9sb2FuID09IDEgfiAiRWR1Y2F0aW9uIiwNCiAgICAgIFRSVUUgfiAiT3RoZXIiDQogICAgKQ0KICApDQoNCiMgQ3JlYXRpbmcgdmFyaWFibGUgRGVmYXVsdF9ZTiB3aGljaCByZWZsZWN0cyBEZWZhdWx0IGFzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUuDQpsb2FuX2RlZmF1bHRfTW9kMDEgPC0gbG9hbl9kZWZhdWx0X01vZDAxICU+JSANCiAgbXV0YXRlKA0KICAgIERlZmF1bHRfWU4gPSBjYXNlX3doZW4oDQogICAgICBEZWZhdWx0ID09IDAgfiAiTm8gRGVmYXVsdCIsDQogICAgICBEZWZhdWx0ID09IDEgfiAiRGVmYXVsdGVkIiwNCiAgICAgIFRSVUUgfiBOQQ0KICAgICkNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBDQVBJVEFMSVpJTkcgQUxMIENBVEVHT1JJQ0FMIFZBUklBQkxFUw0KIyBCeSBkZWZhdWx0LCBzb21lIG9mIHRoZSBjaGFyYWN0ZXIgcmVzcG9uc2VzIGFyZSBsb3dlcmNhc2UNCiMgbG9hbl9kZWZhdWx0X01vZDAyIGlzIGEgY29weSBvZiBsb2FuX2RlZmF1bHRfTW9kMDEgdGhhdCBoYXMgY2hhcmFjdGVyIGVudHJpZXMgd2l0aCB1cHBlcmNhc2UgZmlyc3QgbGV0dGVycw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIENvcHlpbmcgbG9hbl9kZWZhdWx0X01vZDAxDQpsb2FuX2RlZmF1bHRfTW9kMDIgPC0gbG9hbl9kZWZhdWx0X01vZDAxDQoNCiMgU2VsZWN0aW5nIGNoYXJhY3RlciB2YXJpYWJsZXMgYW5kIG1ha2luZyB0aGVpciBmaXJzdCBsZXR0ZXJzIHVwcGVyY2FzZQ0KbG9hbl9kZWZhdWx0X01vZDAyW3NhcHBseShsb2FuX2RlZmF1bHRfTW9kMDIsIHR5cGVvZikgPT0gImNoYXJhY3RlciJdIDwtIA0KICBsb2FuX2RlZmF1bHRfTW9kMDJbc2FwcGx5KGxvYW5fZGVmYXVsdF9Nb2QwMiwgdHlwZW9mKSA9PSAiY2hhcmFjdGVyIl0gJT4lIA0KICBzYXBwbHkoDQogICAgZnVuY3Rpb24oeCkgew0KICAgICAgc3Vic3RyKHgsIDEsIDEpIDwtIHRvdXBwZXIoc3Vic3RyKHgsIDEsIDEpKQ0KICAgICAgeA0KICAgIH0NCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBEQVRBIE1PRElGSUNBVElPTg0KIyBBZGRpbmcgbWlzc2luZyB2YWx1ZXMNCiMgbG9hbl9kZWZhdWx0X01vZDAzIGlzIGEgY29weSBvZiBsb2FuX2RlZmF1bHRfTW9kMDIgd2l0aCBtaXNzaW5nIHZhbHVlcw0KIyBsb2FuX2RlZmF1bHRfTWlzc0xvYyBzaG93cyB0aGUgbG9jYXRpb25zIG9mIGFsbCBtaXNzaW5nIHZhbHVlcyB2aWEgVFJVRQ0KIyBsb2FuX2RlZmF1bHRfVHJ1ZSBzaG93cyBhbGwgb2JzZXJ2YXRpb25zIGZyb20gbG9hbl9kZWZhdWx0X01vZDAyIHRoYXQgd2VyZSBnaXZlbiBtaXNzaW5nIHZhbHVlcyBpbiBsb2FuX2RlZmF1bHRfTW9kMDMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBDb3B5aW5nIGxvYW5fZGVmYXVsdF9Nb2QwMg0KbG9hbl9kZWZhdWx0X01vZDAzIDwtIGxvYW5fZGVmYXVsdF9Nb2QwMg0KDQojIENyZWF0aW5nIHJhbmRvbSBvYnNlcnZhdGlvbiBJRHMgYW5kIHJlcGxhY2luZyB0aGUgY29ycmVzcG9uZGluZyBvYnNlcnZhdGlvbnMgd2l0aCBtaXNzaW5nDQoNCiMgbG9hbl9kZWZhdWx0X01vZDAzJENoZWNraW5nX0Ftb3VudFtzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMyRUZXJtW3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCiMgbG9hbl9kZWZhdWx0X01vZDAzJENyZWRpdF9zY29yZVtzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpsb2FuX2RlZmF1bHRfTW9kMDMkR2VuZGVyW3NhbXBsZSgxOjEwMDAsIDY4LCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KbG9hbl9kZWZhdWx0X01vZDAzJE1hcml0YWxfc3RhdHVzW3NhbXBsZSgxOjEwMDAsIDg3LCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkQ2FyX2xvYW5bc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkUGVyc29uYWxfbG9hbltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMyRIb21lX2xvYW5bc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkRWR1Y2F0aW9uX2xvYW5bc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KbG9hbl9kZWZhdWx0X01vZDAzJEVtcF9zdGF0dXNbc2FtcGxlKDE6MTAwMCwgMTM2LCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkQW1vdW50W3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCiMgbG9hbl9kZWZhdWx0X01vZDAzJFNhdmluZ19hbW91bltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpsb2FuX2RlZmF1bHRfTW9kMDMkRW1wX2R1cmF0aW9uW3NhbXBsZSgxOjEwMDAsIDIwMSwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCmxvYW5fZGVmYXVsdF9Nb2QwMyRBZ2Vbc2FtcGxlKDE6MTAwMCwgMTU5LCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDMkTm9fb2ZfY3JlZGl0X2FjY291bnRbc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KDQojIFNob3dpbmcgdGhlIGxvY2F0aW9ucyBvZiBhbGwgbWlzc2luZyB2YWx1ZXMgdmlhIFRSVUUNCmxvYW5fZGVmYXVsdF9NaXNzTG9jIDwtIHNhcHBseShsb2FuX2RlZmF1bHRfTW9kMDMsIGlzLm5hKQ0KDQojIFNob3dpbmcgYWxsIHJvd3Mgb2YgY29tcGxldGUgZGF0YSB0aGF0IHdlcmUgZ2l2ZW4gbWlzc2luZyB2YWx1ZXMNCmxvYW5fZGVmYXVsdF9UcnVlIDwtIGxvYW5fZGVmYXVsdF9Nb2QwMlsocm93U3VtcyhzYXBwbHkobG9hbl9kZWZhdWx0X01vZDAzLCBpcy5uYSkpID4gMCksIF0NCmBgYA0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgQ0FURUdPUklaSU5HIFZBUklBQkxFUw0KIyBDYXRlZ29yaXppbmcgdmFyaWFibGVzIGluIGxvYW5fZGVmYXVsdF9Nb2QwMyBpbnRvIGJpbmFyeSwgbnVtZXJpYywgYW5kIGNhdGVnb3JpY2FsDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgSWRlbnRpZnlpbmcgdmFyaWFibGUgdHlwZXMNCmxvYW5fZGVmYXVsdF9WYXJUeXBlIDwtIA0KICBsb2FuX2RlZmF1bHRfTW9kMDMgJT4lIA0KICBzYXBwbHkodHlwZW9mKQ0KDQojIElkZW50aWZ5aW5nIGJpbmFyeSB2YXJpYWJsZXMNCiMgVmFyaWFibGVzIHRoYXQgY29udGFpbiAwLCAxLCBvciBOQQ0KbG9hbl9kZWZhdWx0X1ZhclR5cGVbDQogIGFwcGx5KA0KICAgIGxvYW5fZGVmYXVsdF9Nb2QwMywgDQogICAgMiwgDQogICAgZnVuY3Rpb24oeCl7YWxsKG1hdGNoKHgsIGMoMCwgMSwgTkEpLCBub21hdGNoID0gRkFMU0UpKX0NCiAgKQ0KXSA8LSAiQmluYXJ5Ig0KDQojIElkZW50aWZ5aW5nIG51bWVyaWMgdmFyaWFibGVzDQojIFZhcmlhYmxlcyB0aGF0IGFyZSBudW1iZXJzIGJ1dCBub3QgYmluYXJ5DQpsb2FuX2RlZmF1bHRfVmFyVHlwZVtsb2FuX2RlZmF1bHRfVmFyVHlwZSA9PSAiaW50ZWdlciJdIDwtICJOdW1lcmljIg0KDQojIElkZW50aWZ5aW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcw0KIyBWYXJpYWJsZXMgdGhhdCBhcmUgY2hhcmFjdGVycw0KbG9hbl9kZWZhdWx0X1ZhclR5cGVbbG9hbl9kZWZhdWx0X1ZhclR5cGUgPT0gImNoYXJhY3RlciJdIDwtICJDYXRlZ29yaWNhbCINCmBgYA0KDQojIEludHJvZHVjdGlvbg0KDQpXaXRoIHJlYWwtd29ybGQgZGF0YSwgaXQgaXMgY29tbW9uIHRvIGVuY291bnRlciBtaXNzaW5nIHZhbHVlcyByZXN1bHRpbmcgZnJvbSBlbnRyeSBlcnJvciwgbm9uLXJlc3BvbnNlLCBoYXJkd2FyZSBmYWlsdXJlLCBldGMuIEluIHNvbWUgY2FzZXMsIGlnbm9yaW5nIG1pc3NpbmcgdmFsdWVzIG1pZ2h0IG5vdCBtYWtlIGEgc2lnbmlmaWNhbnQgaW1wYWN0IGluIGEgc3R1ZHkncyBjb25jbHVzaW9ucywgYnV0IGluIG90aGVyIGNhc2VzLCBpZ25vcmluZyBtaXNzaW5nIHZhbHVlcyBjYW4gbGVhZCB0byBzaWduaWZpY2FudCBkZXZpYXRpb25zIGZyb20gdGhlIHRydXRoIG9yIGV2ZW4gZmFsc2UgY29uY2x1c2lvbnMuIA0KDQpUaGlzIHByb2dyYW0gc2VydmVzIGFzIGFuIGV4ZXJjaXNlIGluIGFkZHJlc3NpbmcgbWlzc2luZyBkYXRhIGJ5IHVzaW5nIGBCYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdmAgYXMgaXRzIGJhc2UuIEJlY2F1c2UgYEJhbmtMb2FuRGVmYXVsdERhdGFzZXQuY3N2YCBoYXMgbm8gbWlzc2luZyB2YWx1ZXMgdG8gc3RhcnQgd2l0aCwgc29tZSB2YWx1ZXMgd2VyZSByZXBsYWNlZCB3aXRoIGBOQWAgdG8gc2ltdWxhdGUgYW4gaW5jb21wbGV0ZSBkYXRhc2V0Lg0KDQpUaGUgZm9sbG93aW5nIHBsb3RzIHNob3cgd2hpY2ggdmFyaWFibGVzIGNvbnRhaW4gbWlzc2luZyB2YWx1ZXMgYW5kIGhvdyBtYW55LiBIb3ZlcmluZyBvdmVyIGVhY2ggYmFyIGdpdmVzIGZ1cnRoZXIgZGV0YWlscy4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIE1JU1NJTkcgVkFMVUVTDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgR2VuZXJhdGluZyBkYXRhIGZyYW1lIG9mIG1pc3NpbmcgdmFsdWVzIHBlciB2YXJpYWJsZQ0KTWlzc0RhdENvdW50cyA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZXMgPSBuYW1lcyhsb2FuX2RlZmF1bHRfTW9kMDMpLA0KICBWYXJUeXBlID0gbG9hbl9kZWZhdWx0X1ZhclR5cGUsDQogIE1pc3NpbmcgPSBjb2xTdW1zKGlzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwMykpDQopDQoNCiMgR2VuZXJhdGluZyBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIHBsb3RseQ0KUGxvdF9NaXNzaW5nVmFscyA8LSANCiAgIyBUYWtpbmcgYSBzdWJzZXQgb2YgTWlzc0RhdENvdW50cywgc28gb25seSBlbnRyaWVzIHdpdGggPiAwIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgZGlzcGxheWVkDQogIHN1YnNldChNaXNzRGF0Q291bnRzLCBNaXNzaW5nID4gMCkgJT4lIA0KICAjIFBhc3NpbmcgdGhlIHN1YnNldCB0byBwbG90X2x5DQogIHBsb3RfbHkoDQogICAgeCA9IH5WYXJpYWJsZXMsDQogICAgeSA9IH5NaXNzaW5nLA0KICAgIHNwbGl0ID0gflZhclR5cGUsDQogICAgaG92ZXJ0ZW1wbGF0ZSA9IH5wYXN0ZTAoDQogICAgICAiPGI+Q291bnQ8L2I+OiAiLCBNaXNzaW5nLCAiIG9mIDEwMDA8YnI+IiwNCiAgICAgICI8Yj5cVTAwMjUgTWlzc2luZzwvYj46ICIsIHJvdW5kKE1pc3NpbmcgLyAxMDAwICogMTAwLCBkaWdpdHMgPSAzKSwgIlxVMDAyNSINCiAgICApDQogICkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSBsaXN0KA0KICAgICAgdGV4dCA9ICJNaXNzaW5nIFZhbHVlcyBwZXIgVmFyaWFibGUiDQogICAgKSwNCiAgICB4YXhpcyA9IGxpc3QoDQogICAgICB0aXRsZSA9ICJWYXJpYWJsZXMgd2l0aCBNaXNzaW5nIFZhbHVlcyIsDQogICAgICBjYXRlZ29yeW9yZGVyID0gInRyYWNlIg0KICAgICksDQogICAgeWF4aXMgPSBsaXN0KA0KICAgICAgdGl0bGUgPSAiTnVtYmVyIG9mIE1pc3NpbmcgVmFsdWVzIg0KICAgICksDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBWYXJpYWJsZSBUeXBlIDwvYj4iKSwNCiAgICAgIGJnY29sb3IgPSAiI0UyRTJFMiIsDQogICAgICBib3JkZXJjb2xvciA9ICIjRkZGRkZGIiwNCiAgICAgIGJvcmRlcndpZHRoID0gMg0KICAgICkNCiAgKQ0KDQojIE91dHB1dHRpbmcgcGxvdA0KUGxvdF9NaXNzaW5nVmFscw0KYGBgDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBDQVRFR09SSUNBTCBWQVJJQUJMRVMgV0lUSCBNSVNTSU5HIFZBTFVFUw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIFN1YnNldHRpbmcgbG9hbl9kZWZhdWx0X01vZDAzIGludG8gdGhlIHZhcmlhYmxlcyB0aGF0IGFyZSBjYXRlZ29yaWNhbCBhbmQgaGF2ZSBtaXNzaW5nIGRhdGENCkNhdFZhcnNfTWlzcyA8LSANCiAgbG9hbl9kZWZhdWx0X01vZDAzWw0KICAgIChsb2FuX2RlZmF1bHRfVmFyVHlwZSA9PSAiQ2F0ZWdvcmljYWwiKSAmIChjb2xTdW1zKGlzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwMykpID4gMCkNCiAgXQ0KDQpGaWdfRGF0YSA8LSBjKCkNCg0KZm9yKGkgaW4gMTpuY29sKENhdFZhcnNfTWlzcykpIHsNCiAgRmlnX0RhdGFbW2ldXSA8LSBDYXRWYXJzX01pc3MgJT4lIA0KICAgIGNvdW50KC5kYXRhW1tuYW1lcyhDYXRWYXJzX01pc3MpW2ldXV0pDQp9DQoNCiMgUHJlcGFyaW5nIGEgbGlzdCBvZiBzdWJwbG90cw0KTnVtRmlnIDwtIGMoKQ0KIyBVc2luZyBhIGZvciBsb29wIHRvIGdlbmVyYXRlIGEgc3VicGxvdCBwZXIgdmFyaWFibGUgaW4gQ2F0VmFyc19NaXNzDQpmb3IoaSBpbiAxOmxlbmd0aChGaWdfRGF0YSkpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KCkNCiAgZm9yKGogaW4gMTpucm93KEZpZ19EYXRhW1tpXV0pKSB7DQogICAgTnVtRmlnW1tpXV0gPC0gTnVtRmlnW1tpXV0gJT4lIA0KICAgICAgYWRkX3RyYWNlKA0KICAgICAgICB4ID0gbmFtZXMoQ2F0VmFyc19NaXNzKVtpXSwNCiAgICAgICAgeSA9IEZpZ19EYXRhW1tpXV1baiwyXSwgDQogICAgICAgIHR5cGUgPSAiYmFyIiwNCiAgICAgICAgbmFtZSA9IEZpZ19EYXRhW1tpXV1baiwxXSwNCiAgICAgICAgbGVnZW5kZ3JvdXAgPSBuYW1lcyhDYXRWYXJzX01pc3MpW2ldLA0KICAgICAgICBsZWdlbmRncm91cHRpdGxlID0gbGlzdCh0ZXh0ID0gbmFtZXMoQ2F0VmFyc19NaXNzKVtpXSksDQogICAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZTAoDQogICAgICAgICAgIjxiPkNvdW50PC9iPjogIiwgRmlnX0RhdGFbW2ldXVtqLDJdLCAiIG9mIDEwMDA8YnI+IiwNCiAgICAgICAgICAiPGI+UGVyY2VudGFnZTwvYj46ICIsIHJvdW5kKEZpZ19EYXRhW1tpXV1baiwyXSAvIDEwMDAgKiAxMDAsIGRpZ2l0cyA9IDMpLCAiXFUwMDI1Ig0KICAgICAgICApDQogICAgICApICU+JSANCiAgICAgIGxheW91dCgNCiAgICAgICAgeWF4aXMgPSBsaXN0KHJhbmdlID0gYygwLCA3MDApKQ0KICAgICAgKQ0KICB9DQp9DQoNCkZpZ19DYXRWYXJzX01pc3MgPC0gDQogIHN1YnBsb3QoTnVtRmlnW1sxXV0sIE51bUZpZ1tbMl1dLCBOdW1GaWdbWzNdXSwgbnJvd3MgPSAxLCBtYXJnaW4gPSAwLjA1KSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJDYXRlZ29yaWNhbCBWYXJpYWJsZXMgd2l0aCBNaXNzaW5nIFZhbHVlcyIsDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBDYXRlZ29yaWNhbCA8YnI+IFJlc3BvbnNlcyA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApLA0KICAgIHlheGlzID0gbGlzdCgNCiAgICAgIHRpdGxlID0gIk51bWJlciBvZiBFbnRyaWVzIg0KICAgICkNCiAgKQ0KDQpGaWdfQ2F0VmFyc19NaXNzDQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIE5VTUVSSUMgVkFSSUFCTEVTIFdJVEggTUlTU0lORyBWQUxVRVMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTdWJzZXR0aW5nIGxvYW5fZGVmYXVsdF9Nb2QwMyBpbnRvIHRoZSB2YXJpYWJsZXMgdGhhdCBhcmUgY2F0ZWdvcmljYWwgYW5kIGhhdmUgbWlzc2luZyBkYXRhDQpOdW1WYXJzX01pc3MgPC0gDQogIGxvYW5fZGVmYXVsdF9Nb2QwM1sNCiAgICAobG9hbl9kZWZhdWx0X1ZhclR5cGUgPT0gIk51bWVyaWMiKSAmIChjb2xTdW1zKGlzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwMykpID4gMCkNCiAgXSANCg0KTnVtVmFyc19NaXNzX01vZDAxIDwtIE51bVZhcnNfTWlzcyAlPiUgDQogIHNhcHBseShpcy5uYSkgJT4lIA0KICBpZmVsc2UoTkEsICJOb24tbWlzc2luZyIpICU+JSANCiAgZGF0YS5mcmFtZSgpDQoNCkZpZ19EYXRhIDwtIGMoKQ0KDQpmb3IoaSBpbiAxOm5jb2woTnVtVmFyc19NaXNzX01vZDAxKSkgew0KICBGaWdfRGF0YVtbaV1dIDwtIE51bVZhcnNfTWlzc19Nb2QwMSAlPiUgDQogICAgY291bnQoLmRhdGFbW25hbWVzKE51bVZhcnNfTWlzc19Nb2QwMSlbaV1dXSkNCn0NCg0KIyBQcmVwYXJpbmcgYSBsaXN0IG9mIHN1YnBsb3RzDQpOdW1GaWcgPC0gYygpDQojIFVzaW5nIGEgZm9yIGxvb3AgdG8gZ2VuZXJhdGUgYSBzdWJwbG90IHBlciB2YXJpYWJsZSBpbiBOdW1WYXJzX01pc3NfTW9kMDENCmZvcihpIGluIDE6bGVuZ3RoKEZpZ19EYXRhKSl7DQogIE51bUZpZ1tbaV1dIDwtIHBsb3RfbHkoKQ0KICBmb3IoaiBpbiAxOm5yb3coRmlnX0RhdGFbW2ldXSkpIHsNCiAgICBOdW1GaWdbW2ldXSA8LSBOdW1GaWdbW2ldXSAlPiUgDQogICAgICBhZGRfdHJhY2UoDQogICAgICAgIHggPSBuYW1lcyhOdW1WYXJzX01pc3NfTW9kMDEpW2ldLA0KICAgICAgICB5ID0gRmlnX0RhdGFbW2ldXVtqLDJdLCANCiAgICAgICAgdHlwZSA9ICJiYXIiLA0KICAgICAgICBuYW1lID0gRmlnX0RhdGFbW2ldXVtqLDFdLA0KICAgICAgICBsZWdlbmRncm91cCA9IG5hbWVzKE51bVZhcnNfTWlzc19Nb2QwMSlbaV0sDQogICAgICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSBuYW1lcyhOdW1WYXJzX01pc3NfTW9kMDEpW2ldKSwNCiAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlMCgNCiAgICAgICAgICAiPGI+Q291bnQ8L2I+OiAiLCBGaWdfRGF0YVtbaV1dW2osMl0sICIgb2YgMTAwMDxicj4iLA0KICAgICAgICAgICI8Yj5QZXJjZW50YWdlPC9iPjogIiwgcm91bmQoRmlnX0RhdGFbW2ldXVtqLDJdIC8gMTAwMCAqIDEwMCwgZGlnaXRzID0gMyksICJcVTAwMjUiDQogICAgICAgICkNCiAgICAgICkgJT4lIA0KICAgICAgbGF5b3V0KA0KICAgICAgICB5YXhpcyA9IGxpc3QocmFuZ2UgPSBjKDAsIDg1MCkpDQogICAgICApDQogIH0NCn0NCg0KRmlnX051bVZhcnNfTWlzcyA8LSANCiAgc3VicGxvdChOdW1GaWdbWzFdXSwgTnVtRmlnW1syXV0sIG5yb3dzID0gMSwgbWFyZ2luID0gMC4wNSkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiTnVtZXJpY2FsIFZhcmlhYmxlcyB3aXRoIE1pc3NpbmcgVmFsdWVzIiwNCiAgICBsZWdlbmQgPSBsaXN0KA0KICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiPGI+IFJlc3BvbnNlcyA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApLA0KICAgIHlheGlzID0gbGlzdCgNCiAgICAgIHRpdGxlID0gIk51bWJlciBvZiBFbnRyaWVzIg0KICAgICkNCiAgKQ0KDQpGaWdfTnVtVmFyc19NaXNzDQpgYGANCg0KIyMgSW1wdXRhdGlvbg0KDQpJbXB1dGF0aW9uIGlzIHRoZSBwcm9jZXNzIG9mIHJlcGxhY2luZyBtaXNzaW5nIGRhdGEgd2l0aCBzdWJzdGl0dXRlIHZhbHVlcy4gSWRlYWxseSwgd2Ugd2FudCB0byBpbXB1dGUgdmFsdWVzIGFzIGNsb3NlIGFzIHBvc3NpYmxlIHRvIHRoZSB0cnVlIHZhbHVlIHRoYXQgaXMgbWlzc2luZy4gVG8gdGhhdCBlbmQsIHdlIG11c3QgdHJ5IHRvIHByZWRpY3Qgd2hhdCB0aGUgbWlzc2luZyB2YWx1ZSB3b3VsZCBoYXZlIGJlZW4uDQoNCldlIHdpbGwgZXhwbG9yZSB0aGUgZm9sbG93aW5nIG1ldGhvZHMgb2YgaW1wdXRhdGlvbjoNCg0KKiBrLU5lYXJlc3QgTmVpZ2hib3JzICgqayotTk4pIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aCBtaXNzaW5nIHZhbHVlcw0KKiBSYW5kb20gcmVncmVzc2lvbiBpbXB1dGF0aW9uIGZvciBudW1lcmljYWwgdmFyaWFibGVzIHdpdGggbWlzc2luZyB2YWx1ZXMNCiogTXVsdGlwbGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucyAoTUlDRSkgZm9yIGFsbCBtaXNzaW5nIHZhbHVlcw0KDQojIyBGZWF0dXJlIEVuZ2luZWVyaW5nDQoNCkZlYXR1cmUgZW5naW5lZXJpbmcgaXMgdGhlIHByb2Nlc3Mgb2Ygc2VsZWN0aW5nLCB0cmFuc2Zvcm1pbmcsIGFuZCBjcmVhdGluZyBmZWF0dXJlIHZhcmlhYmxlcyB0byBpbXByb3ZlIHRoZSBwZXJmb3JtYW5jZSBvZiBwcmVkaWN0aXZlIG1vZGVscy4NCg0KRm9yIHRoZSBwdXJwb3NlIG9mIGZlYXR1cmUgc2VsZWN0aW9uLCB3ZSB1c2UgYSB3cmFwcGVyIG1ldGhvZCwgd2hpY2ggY29uc2lzdHMgb2YgdXNpbmcgYSBwcmVkaWN0aXZlIG1vZGVsIHRvIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIGZlYXR1cmVzIHRoZW4gc2VsZWN0aW5nIHRoZSBoaWdoZXN0IHBlcmZvcm1pbmcgc2V0IG9mIGZlYXR1cmVzLiBUaGUgYGNhcmV0YCBwYWNrYWdlIGVuYWJsZXMgdXMgdG8gdXNlIHJlY3Vyc2l2ZSBmZWF0dXJlIGVsaW1pbmF0aW9uIChSRkUpIHRvIG91dHB1dCB0aGUgbGlzdCBvZiBmZWF0dXJlcyB0aGF0IGdpdmUgdXMgdGhlIGJlc3QgcGVyZm9ybWFuY2UuIFRoZSBmb2xsb3dpbmcgb3V0cHV0IGdpdmVzIHVzIHRoZSBiZXN0IHNldCBvZiBmZWF0dXJlIHZhcmlhYmxlcyB0byBwcmVkaWN0IHRoZSBsYWJlbCB2YXJpYWJsZSwgYERlZmF1bHRgLg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgRkVBVFVSRSBTRUxFQ1RJT04NCiMgRHIuIFBlbmcncyBjb2RlIHdhcyB1c2VkIGFzIGEgcmVmZXJlbmNlDQojID09PT09PT09PT09PT09PT09PT09DQoNCnJlc3VsdHMgPC0gcmZlKA0KICBsb2FuX2RlZmF1bHRbLCAtMV0sIA0KICBsb2FuX2RlZmF1bHQkRGVmYXVsdCwgDQogIHNpemVzID0gYygxOjUpLCANCiAgcmZlQ29udHJvbCA9IHJmZUNvbnRyb2woZnVuY3Rpb25zID0gcmZGdW5jcywgbWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTApDQogICkNCg0KcHJlZGljdG9ycyhyZXN1bHRzKQ0KYGBgDQoNCkFzIFJGRSBoYXMgcmV0dXJuZWQgYWxsIG9mIG91ciBvcmlnaW5hbCBmZWF0dXJlIHZhcmlhYmxlcywgd2UgcHJvY2VlZCB3aXRob3V0IGVsaW1pbmF0aW5nIGFueSBmZWF0dXJlcyBmcm9tIGZ1cnRoZXIgYW5hbHlzaXMuDQoNClZpc3VhbGl6aW5nIGFsbCBudW1lcmljYWwgbm9uLWJpbmFyeSB2YXJpYWJsZXMsIHdlIGNhbiBzZWUgdGhhdDoNCg0KKiBUaGUgbmFycm93IG51bWVyaWNhbCByYW5nZSBhbmQgc3RlZXAgZHJvcHMgb2YgYE5vX29mX2NyZWRpdF9hY2NgIG1ha2UgaXQgYSBwcmltZSBjYW5kaWRhdGUgZm9yIGJpbm5pbmcuDQoqIFRoZSByZW1haW5pbmcgbnVtZXJpY2FsIHZhcmlhYmxlcyBhcmUgY2FuZGlkYXRlcyBmb3Igc3RhbmRhcmRpemF0aW9uIHRvIGJldHRlciBzdXBwb3J0IGZ1dHVyZSBtb2RlbGluZy4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIEFMTCBOVU1FUklDQUwgTk9OLUJJTkFSWSBWQVJJQUJMRVMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTZWxlY3Rpbmcgb25seSBudW1lcmljIHZhcmlhYmxlcw0KTnVtVmFycyA8LSBzZWxlY3QobG9hbl9kZWZhdWx0X01vZDAzLCB3aGVyZShpcy5udW1lcmljKSkNCiMgU2VsZWN0aW5nIGVsaW1pbmF0aW5nIGFueSBiaW5hcnkgdmFyaWFibGVzDQpOdW1WYXJzIDwtIE51bVZhcnNbIWFwcGx5KE51bVZhcnMsIDIsIGZ1bmN0aW9uKHgpe2FsbChtYXRjaCh4LCBjKDAsIDEsIE5BKSwgbm9tYXRjaCA9IEZBTFNFKSl9KV0NCg0KIyBQcmVwYXJpbmcgYSBsaXN0IG9mIHN1YnBsb3RzDQpOdW1GaWcgPC0gYygpDQojIFVzaW5nIGEgZm9yIGxvb3AgdG8gZ2VuZXJhdGUgYSBzdWJwbG90IHBlciB2YXJpYWJsZSBpbiBOdW1WYXJzDQpmb3IoaSBpbiAxOmxlbmd0aChuYW1lcyhOdW1WYXJzKSkpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KA0KICAgIHggPSBOdW1WYXJzW1tpXV0sIA0KICAgICMgeSA9ICIiLCANCiAgICB0eXBlID0gImhpc3RvZ3JhbSIsDQogICAgbmFtZSA9IGNvbG5hbWVzKE51bVZhcnMpW2ldDQogICkNCn0NCg0KIyBHZW5lcmF0aW5nIGEgcGxvdCB0aGF0IGNvbnRhaW5zIDggc3VicGxvdHMgKG9uZSBmb3IgZWFjaCB2YXJpYWJsZSBpbiBOdW1WYXJzKSBhY3Jvc3MgNCByb3dzDQpQbG90X051bVZhcnMgPC0gDQogIHN1YnBsb3QoTnVtRmlnW1sxXV0sIE51bUZpZ1tbMl1dLCBOdW1GaWdbWzNdXSwgTnVtRmlnW1s0XV0sIE51bUZpZ1tbNV1dLCBOdW1GaWdbWzZdXSwgTnVtRmlnW1s3XV0sIE51bUZpZ1tbOF1dLCBucm93cyA9IDQsIG1hcmdpbiA9IDAuMDUpICU+JSANCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbnMgb2YgQWxsIE51bWVyaWNhbCBOb24tYmluYXJ5IFZhcmlhYmxlcyIsDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBWYXJpYWJsZSA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfTnVtVmFycw0KYGBgDQoNCkJpbm5pbmcgYE5vX29mX2NyZWRpdF9hY2NgIGFjY29yZGluZyB0byB0aGUgc2hhcnAgZHJvcHMgaW4gdGhlIGhpc3RvZ3JhbSByZXN1bHRzIGluIHRoZSBjcmVhdGlvbiBvZiBuZXcgdmFyaWFibGUgYE5vX29mX2NyZWRpdF9hY2NfQmluc2Agd2l0aCB0aGUgZm9sbG93aW5nIGJyZWFrZG93bjoNCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIEJJTk5JTkcgTm9fb2ZfY3JlZGl0X2FjYw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIFRyYW5zZm9ybWluZyBOb19vZl9jcmVkaXRfYWNjIGludG8gb3JkaW5hbCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMNCmxvYW5fZGVmYXVsdF9Nb2QwNCA8LSBsb2FuX2RlZmF1bHRfTW9kMDMgJT4lIA0KICBtdXRhdGUoDQogICAgTm9fb2ZfY3JlZGl0X2FjY19CaW5zID0gY3V0KA0KICAgICAgTm9fb2ZfY3JlZGl0X2FjYywNCiAgICAgIGJyZWFrcyA9IGMoMSwgMiwgNSwgOSksDQogICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUNCiAgICApDQogICkNCg0KdGFibGUobG9hbl9kZWZhdWx0X01vZDA0JE5vX29mX2NyZWRpdF9hY2NfQmlucykNCmBgYA0KDQpTdGFuZGFyZGl6YXRpb24gb2YgdGhlIHJlbWFpbmluZyBzZXZlbiBudW1lcmljYWwgdmFyaWFibGVzIHJlc3VsdHMgaW4gdGhlIGZvbGxvd2luZyBkaXN0cmlidXRpb246DQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBGRUFUVVJFIFNUQU5EQVJESVpBVElPTg0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIE5vcm1hbGl6YXRpb24gZnVuY3Rpb24gZnJvbSBEci4gUGVuZw0Kc3RhbmRhcmRpemUgPC0gZnVuY3Rpb24oeCkgew0KICByZXR1cm4oKHggLSBtZWFuKHgsIG5hLnJtID0gVFJVRSkpIC8gc2QoeCwgbmEucm0gPSBUUlVFKSkNCn0NCg0KIyBOb3JtYWxpemluZyBudW1lcmljIHZhcmlhYmxlcw0KbG9hbl9kZWZhdWx0X01vZDA0JENoZWNraW5nX2Ftb3VudF9TdGFuZCA8LSANCiAgc3RhbmRhcmRpemUobG9hbl9kZWZhdWx0X01vZDA0JENoZWNraW5nX2Ftb3VudCkNCmxvYW5fZGVmYXVsdF9Nb2QwNCRUZXJtX1N0YW5kIDwtIA0KICBzdGFuZGFyZGl6ZShsb2FuX2RlZmF1bHRfTW9kMDQkVGVybSkNCmxvYW5fZGVmYXVsdF9Nb2QwNCRDcmVkaXRfc2NvcmVfU3RhbmQgPC0gDQogIHN0YW5kYXJkaXplKGxvYW5fZGVmYXVsdF9Nb2QwNCRDcmVkaXRfc2NvcmUpDQpsb2FuX2RlZmF1bHRfTW9kMDQkQW1vdW50X1N0YW5kIDwtIA0KICBzdGFuZGFyZGl6ZShsb2FuX2RlZmF1bHRfTW9kMDQkQW1vdW50KQ0KbG9hbl9kZWZhdWx0X01vZDA0JFNhdmluZ19hbW91bnRfU3RhbmQgPC0gDQogIHN0YW5kYXJkaXplKGxvYW5fZGVmYXVsdF9Nb2QwNCRTYXZpbmdfYW1vdW50KQ0KbG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbl9TdGFuZCA8LSANCiAgc3RhbmRhcmRpemUobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbikNCmxvYW5fZGVmYXVsdF9Nb2QwNCRBZ2VfU3RhbmQgPC0gDQogIHN0YW5kYXJkaXplKGxvYW5fZGVmYXVsdF9Nb2QwNCRBZ2UpDQoNCiMgTGlzdCBvZiBzdGFuZGFyZGl6ZWQgdmFyaWFibGVzDQpTdGFuZFZhcnMgPC0gYygiQ2hlY2tpbmdfYW1vdW50X1N0YW5kIiwgIlRlcm1fU3RhbmQiLCAiQ3JlZGl0X3Njb3JlX1N0YW5kIiwgIkFtb3VudF9TdGFuZCIsICJTYXZpbmdfYW1vdW50X1N0YW5kIiwgIkVtcF9kdXJhdGlvbl9TdGFuZCIsICJBZ2VfU3RhbmQiKQ0KDQojIE51bWVyaWMgdmFyaWFibGUgZGF0YSBmb3IgZGUtc3RhbmRhcmRpemF0aW9uDQpEZVN0YW5kVmFyRGF0YSA8LSANCiAgZGF0YS5mcmFtZSgNCiAgICBWYXJpYWJsZSA9IGMoIkNoZWNraW5nX2Ftb3VudCIsICJUZXJtIiwgIkNyZWRpdF9zY29yZSIsICJBbW91bnQiLCAiU2F2aW5nX2Ftb3VudCIsICJFbXBfZHVyYXRpb24iLCAiQWdlIiksDQogICAgTWVhbiA9IGMoDQogICAgICBtZWFuKGxvYW5fZGVmYXVsdF9Nb2QwNCRDaGVja2luZ19hbW91bnQsIG5hLnJtID0gVFJVRSksDQogICAgICBtZWFuKGxvYW5fZGVmYXVsdF9Nb2QwNCRUZXJtLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWVhbihsb2FuX2RlZmF1bHRfTW9kMDQkQ3JlZGl0X3Njb3JlLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWVhbihsb2FuX2RlZmF1bHRfTW9kMDQkQW1vdW50LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWVhbihsb2FuX2RlZmF1bHRfTW9kMDQkU2F2aW5nX2Ftb3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICAgIG1lYW4obG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbiwgbmEucm0gPSBUUlVFKSwNCiAgICAgIG1lYW4obG9hbl9kZWZhdWx0X01vZDA0JEFnZSwgbmEucm0gPSBUUlVFKQ0KICAgICksDQogICAgU0QgPSBjKA0KICAgICAgc2QobG9hbl9kZWZhdWx0X01vZDA0JENoZWNraW5nX2Ftb3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICAgIHNkKGxvYW5fZGVmYXVsdF9Nb2QwNCRUZXJtLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgc2QobG9hbl9kZWZhdWx0X01vZDA0JENyZWRpdF9zY29yZSwgbmEucm0gPSBUUlVFKSwNCiAgICAgIHNkKGxvYW5fZGVmYXVsdF9Nb2QwNCRBbW91bnQsIG5hLnJtID0gVFJVRSksDQogICAgICBzZChsb2FuX2RlZmF1bHRfTW9kMDQkU2F2aW5nX2Ftb3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICAgIHNkKGxvYW5fZGVmYXVsdF9Nb2QwNCRFbXBfZHVyYXRpb24sIG5hLnJtID0gVFJVRSksDQogICAgICBzZChsb2FuX2RlZmF1bHRfTW9kMDQkQWdlLCBuYS5ybSA9IFRSVUUpDQogICAgKQ0KICApDQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIEFMTCBTVEFOREFSRElaRUQgTlVNRVJJQ0FMIFZBUklBQkxFUw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIFNlbGVjdGluZyBvbmx5IHN0YW5kYXJkaXplZCB2YXJpYWJsZXMNClN0YW5kTnVtVmFycyA8LSBsb2FuX2RlZmF1bHRfTW9kMDRbU3RhbmRWYXJzXQ0KDQojIFByZXBhcmluZyBhIGxpc3Qgb2Ygc3VicGxvdHMNCk51bUZpZyA8LSBjKCkNCiMgVXNpbmcgYSBmb3IgbG9vcCB0byBnZW5lcmF0ZSBhIHN1YnBsb3QgcGVyIHZhcmlhYmxlIGluIFN0YW5kTnVtVmFycw0KZm9yKGkgaW4gMTpsZW5ndGgobmFtZXMoU3RhbmROdW1WYXJzKSkpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KA0KICAgIHggPSBTdGFuZE51bVZhcnNbW2ldXSwgDQogICAgIyB5ID0gIiIsIA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIiwNCiAgICBuYW1lID0gY29sbmFtZXMoU3RhbmROdW1WYXJzKVtpXQ0KICApDQp9DQoNCiMgR2VuZXJhdGluZyBhIHBsb3QgdGhhdCBjb250YWlucyA4IHN1YnBsb3RzIChvbmUgZm9yIGVhY2ggdmFyaWFibGUgaW4gU3RhbmROdW1WYXJzKSBhY3Jvc3MgNCByb3dzDQpQbG90X1N0YW5kTnVtVmFycyA8LSANCiAgc3VicGxvdChOdW1GaWdbWzFdXSwgTnVtRmlnW1syXV0sIE51bUZpZ1tbM11dLCBOdW1GaWdbWzRdXSwgTnVtRmlnW1s1XV0sIE51bUZpZ1tbNl1dLCBOdW1GaWdbWzddXSwgbnJvd3MgPSA0LCBtYXJnaW4gPSAwLjA1KSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb25zIG9mIEFsbCBTdGFuZGFyZGl6ZWQgTnVtZXJpY2FsIFZhcmlhYmxlcyIsDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBWYXJpYWJsZSA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfU3RhbmROdW1WYXJzDQpgYGANCg0KIyBSZXBsYWNlbWVudCBJbXB1dGF0aW9uIGZvciBDYXRlZ29yaWNhbCBGZWF0dXJlcw0KDQpUaGUgZGF0YSBjb250YWlucyBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgdmFyaWFibGVzIGBHZW5kZXJgLCBgTWFyaXRhbF9zdGF0dXNgLCBhbmQgYEVtcF9zdGF0dXNgLCBhbmQgdGhlc2UgbWlzc2luZyB2YWx1ZXMgbXVzdCBiZSBhZGRyZXNzZWQgYmVmb3JlIHdlIGNhbiBwcm9jZWVkIHdpdGggbW9kZWxpbmcuIFRvIHRoYXQgZW5kLCB3ZSB1c2UgKmsqLU5OIHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMuIFRoZSByZXN1bHRzIG9mIHRoaXMgaW1wdXRhdGlvbiBjYW4gYmUgc2VlbiBpbiB0aGUgZm9sbG93aW5nIHBsb3Q6DQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBJTVBVVElORyBDQVRFR09SSUNBTCBWQUxVRVMgV0lUSCBrTk4NCiMgQ2F0SW1wdXRlIGlzIGxvYW5fZGVmYXVsdF9Nb2QwNCB3aXRoIGltcHV0ZWQgY2F0ZWdvcmljYWwgdmFsdWVzDQojID09PT09PT09PT09PT09PT09PT09DQoNCkNhdEltcHV0ZSA8LSBrTk4oDQogIGxvYW5fZGVmYXVsdF9Nb2QwNFtSYXdWYXJzXSwgDQogIHZhcmlhYmxlID0gYygiR2VuZGVyIiwgIk1hcml0YWxfc3RhdHVzIiwgIkVtcF9zdGF0dXMiKSwNCiAgayA9IDUNCikNCmBgYA0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgUExPVFRJTkcgQ0FURUdPUklDQUwgVkFSSUFCTEVTIFdJVEggSU1QVVRFRCBWQUxVRVMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTdWJzZXR0aW5nIGxvYW5fZGVmYXVsdF9Nb2QwNCBpbnRvIHRoZSB2YXJpYWJsZXMgdGhhdCBhcmUgY2F0ZWdvcmljYWwgYW5kIGhhdmUgbWlzc2luZyBkYXRhDQpDYXRWYXJzX0ltcCA8LSBDYXRJbXB1dGVbDQogIGMoIkdlbmRlciIsICJNYXJpdGFsX3N0YXR1cyIsICJFbXBfc3RhdHVzIiwgDQogICAgIkdlbmRlcl9pbXAiLCAiTWFyaXRhbF9zdGF0dXNfaW1wIiwgIkVtcF9zdGF0dXNfaW1wIikNCl0NCg0KQ2F0VmFyc19JbXAgPC0gQ2F0VmFyc19JbXAgJT4lIA0KICBtdXRhdGUoDQogICAgR2VuZGVyID0gY2FzZV93aGVuKA0KICAgICAgR2VuZGVyX2ltcCA9PSBUUlVFIH4gcGFzdGUwKEdlbmRlciwgIiAoSW1wdXRlZCkiKSwNCiAgICAgIFRSVUUgfiBHZW5kZXINCiAgICApLA0KICAgIE1hcml0YWxfc3RhdHVzID0gY2FzZV93aGVuKA0KICAgICAgTWFyaXRhbF9zdGF0dXNfaW1wID09IFRSVUUgfiBwYXN0ZTAoTWFyaXRhbF9zdGF0dXMsICIgKEltcHV0ZWQpIiksDQogICAgICBUUlVFIH4gTWFyaXRhbF9zdGF0dXMNCiAgICApLA0KICAgIEVtcF9zdGF0dXMgPSBjYXNlX3doZW4oDQogICAgICBFbXBfc3RhdHVzX2ltcCA9PSBUUlVFIH4gcGFzdGUwKEVtcF9zdGF0dXMsICIgKEltcHV0ZWQpIiksDQogICAgICBUUlVFIH4gRW1wX3N0YXR1cw0KICAgICksDQogICkNCg0KRmlnX0RhdGEgPC0gYygpDQoNCmZvcihpIGluIDE6bmNvbChDYXRWYXJzX0ltcCkpIHsNCiAgRmlnX0RhdGFbW2ldXSA8LSBDYXRWYXJzX0ltcCAlPiUgDQogICAgY291bnQoLmRhdGFbW25hbWVzKENhdFZhcnNfSW1wKVtpXV1dKQ0KfQ0KDQojIFByZXBhcmluZyBhIGxpc3Qgb2Ygc3VicGxvdHMNCk51bUZpZyA8LSBjKCkNCiMgVXNpbmcgYSBmb3IgbG9vcCB0byBnZW5lcmF0ZSBhIHN1YnBsb3QgcGVyIHZhcmlhYmxlIGluIENhdFZhcnNfSW1wDQpmb3IoaSBpbiAxOjMpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KCkNCiAgZm9yKGogaW4gMTpucm93KEZpZ19EYXRhW1tpXV0pKSB7DQogICAgTnVtRmlnW1tpXV0gPC0gTnVtRmlnW1tpXV0gJT4lIA0KICAgICAgYWRkX3RyYWNlKA0KICAgICAgICB4ID0gbmFtZXMoQ2F0VmFyc19JbXApW2ldLA0KICAgICAgICB5ID0gRmlnX0RhdGFbW2ldXVtqLDJdLCANCiAgICAgICAgdHlwZSA9ICJiYXIiLA0KICAgICAgICBuYW1lID0gRmlnX0RhdGFbW2ldXVtqLDFdLA0KICAgICAgICBsZWdlbmRncm91cCA9IG5hbWVzKENhdFZhcnNfSW1wKVtpXSwNCiAgICAgICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9IG5hbWVzKENhdFZhcnNfSW1wKVtpXSksDQogICAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZTAoDQogICAgICAgICAgIjxiPkNvdW50PC9iPjogIiwgRmlnX0RhdGFbW2ldXVtqLDJdLCAiIG9mIDEwMDA8YnI+IiwNCiAgICAgICAgICAiPGI+UGVyY2VudGFnZTwvYj46ICIsIHJvdW5kKEZpZ19EYXRhW1tpXV1baiwyXSAvIDEwMDAgKiAxMDAsIGRpZ2l0cyA9IDMpLCAiXFUwMDI1Ig0KICAgICAgICApDQogICAgICApDQogIH0NCn0NCg0KRmlnX0NhdFZhcnNfSW1wIDwtIA0KICBzdWJwbG90KE51bUZpZ1tbMV1dLCBOdW1GaWdbWzJdXSwgTnVtRmlnW1szXV0sIG5yb3dzID0gMSwgbWFyZ2luID0gMC4wNSwgc2hhcmVZID0gVFJVRSkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiQ2F0ZWdvcmljYWwgVmFyaWFibGVzIHdpdGggTWlzc2luZyBWYWx1ZXMiLA0KICAgIGxlZ2VuZCA9IGxpc3QoDQogICAgICB0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj4gQ2F0ZWdvcmljYWwgPGJyPiBSZXNwb25zZXMgPC9iPiIpLA0KICAgICAgYmdjb2xvciA9ICIjRTJFMkUyIiwNCiAgICAgIGJvcmRlcmNvbG9yID0gIiNGRkZGRkYiLA0KICAgICAgYm9yZGVyd2lkdGggPSAyDQogICAgKSwNCiAgICB5YXhpcyA9IGxpc3QoDQogICAgICB0aXRsZSA9ICJOdW1iZXIgb2YgRW50cmllcyINCiAgICApDQogICkNCg0KRmlnX0NhdFZhcnNfSW1wDQpgYGANCg0KVGhlIHJlc3VsdHMgb2YgdGhpcyBtZXRob2Qgb2YgaW1wdXRhdGlvbiByb3VnaGx5IGFwcGVhciB0byByZWZsZWN0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG5vbi1taXNzaW5nIGRhdGEuIFRoaXMgd291bGQgbWFrZSBzZW5zZSBpZiB0aGUgZGF0YSBpcyBtaXNzaW5nIGF0IHJhbmRvbS4NCg0KIyBSYW5kb20gUmVncmVzc2lvbi1iYXNlZCBJbXB1dGF0aW9uIGZvciBOdW1lcmljYWwgRmVhdHVyZXMNCg0KVGhlIGRhdGEgY29udGFpbnMgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIHZhcmlhYmxlcyBgRW1wX2R1cmF0aW9uYCBhbmQgYEFnZWAsIGFuZCB0aGVzZSBtaXNzaW5nIHZhbHVlcyBtdXN0IGJlIGFkZHJlc3NlZCBiZWZvcmUgd2UgY2FuIHByb2NlZWQgd2l0aCBtb2RlbGluZy4gVG8gdGhhdCBlbmQsIHdlIHVzZSByYW5kb20gcmVncmVzc2lvbiB0byBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzLg0KDQpXZSBzZWxlY3RlZCB0aGUgdmFyaWFibGUgdG8gcmVmZXJlbmNlIGJ5IGxvb2tpbmcgYXQgdGhlIGNvcnJlbGF0aW9uIHBsb3QgYmV0d2VlbiBudW1lcmljYWwgdmFyaWFibGVzLiBCeSBoYXZpbmcgdGhlIGhpZ2hlc3QgY29ycmVsYXRpb25zLCBgTm9fb2ZfY3JlZGl0X2FjY2Agd2FzIGNob3NlbiBmb3IgYEVtcF9kdXJhdGlvbmAgYW5kIGBTYXZpbmdfYW1vdW50YCB3YXMgY2hvc2VuIGZvciBgQWdlYC4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIENPUlJFTEFUSU9OIEJFVFdFRU4gTlVNRVJJQ0FMIFZBUklBQkxFUw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQpjb3JycGxvdCgNCiAgY29yKA0KICAgIGxvYW5fZGVmYXVsdF9Nb2QwNFsNCiAgICAgIGMoDQogICAgICAgICMgIkRlZmF1bHQiLA0KICAgICAgICAiQ2hlY2tpbmdfYW1vdW50IiwNCiAgICAgICAgIlRlcm0iLA0KICAgICAgICAiQ3JlZGl0X3Njb3JlIiwNCiAgICAgICAgIyAiR2VuZGVyIiwNCiAgICAgICAgIyAiTWFyaXRhbF9zdGF0dXMiLA0KICAgICAgICAjICJDYXJfbG9hbiIsDQogICAgICAgICMgIlBlcnNvbmFsX2xvYW4iLA0KICAgICAgICAjICJIb21lX2xvYW4iLA0KICAgICAgICAjICJFZHVjYXRpb25fbG9hbiIsDQogICAgICAgICMgIkVtcF9zdGF0dXMiLA0KICAgICAgICAiQW1vdW50IiwNCiAgICAgICAgIlNhdmluZ19hbW91bnQiLA0KICAgICAgICAiRW1wX2R1cmF0aW9uIiwNCiAgICAgICAgIkFnZSIsDQogICAgICAgICJOb19vZl9jcmVkaXRfYWNjIg0KICAgICAgKQ0KICAgIF0sDQogICAgdXNlID0gImNvbXBsZXRlLm9icyINCiAgKSwNCiAgdHlwZSA9ICJsb3dlciIsDQogIHRsLnNydCA9IDM1LA0KICBhZGRDb2VmLmNvbCA9ICdncmV5NTAnDQopDQpgYGANCg0KVGhlIGZvbGxvd2luZyBpcyB0aGUgcmVzdWx0IG9mIHRoZSBpbXB1dGF0aW9uOg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgSU1QVVRJTkcgTlVNRVJJQ0FMIFZBTFVFUyBXSVRIIExJTkVBUiBSRUdSRVNTSU9ODQojIE51bUltcHV0ZSBpcyBsb2FuX2RlZmF1bHRfTW9kMDQgd2l0aCBpbXB1dGVkIG51bWVyaWNhbCB2YWx1ZXMNCiMgRHIuIFBlbmcncyBjb2RlIHdhcyB1c2VkIGFzIGEgcmVmZXJlbmNlDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgR2VuZXJhdGluZyBsaW5lYXIgaW1wdXRhdGlvbiBtb2RlbCBmb3IgRW1wX2R1cmF0aW9uDQpOdW1JbXB1dGVfTW9kZWxfRW1wX2R1cmF0aW9uIDwtIGxtKA0KICBFbXBfZHVyYXRpb24gfiANCiAgICAjIERlZmF1bHQgKyANCiAgICAjIENoZWNraW5nX2Ftb3VudCArIA0KICAgICMgVGVybSArIA0KICAgICMgQ3JlZGl0X3Njb3JlICsgDQogICAgIyAjIEdlbmRlciArIA0KICAgICMgIyBNYXJpdGFsX3N0YXR1cyArIA0KICAgICMgQ2FyX2xvYW4gKyANCiAgICAjIFBlcnNvbmFsX2xvYW4gKyANCiAgICAjIEhvbWVfbG9hbiArIA0KICAgICMgRWR1Y2F0aW9uX2xvYW4gKyANCiAgICAjICMgRW1wX3N0YXR1cyArIA0KICAgICMgQW1vdW50ICsgDQogICAgIyBTYXZpbmdfYW1vdW50ICsgDQogICAgIyBFbXBfZHVyYXRpb24gKw0KICAgICMgQWdlICsgDQogICAgTm9fb2ZfY3JlZGl0X2FjYywNCiAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwNA0KKQ0KDQojIEdlbmVyYXRpbmcgbGluZWFyIGltcHV0YXRpb24gbW9kZWwgZm9yIEFnZQ0KTnVtSW1wdXRlX01vZGVsX0FnZSA8LSBsbSgNCiAgQWdlIH4gDQogICAgIyBEZWZhdWx0ICsgDQogICAgIyBDaGVja2luZ19hbW91bnQgKyANCiAgICAjIFRlcm0gKyANCiAgICAjIENyZWRpdF9zY29yZSArDQogICAgIyBHZW5kZXIgKyANCiAgICAjIE1hcml0YWxfc3RhdHVzICsgDQogICAgIyBDYXJfbG9hbiArIA0KICAgICMgUGVyc29uYWxfbG9hbiArIA0KICAgICMgSG9tZV9sb2FuICsgDQogICAgIyBFZHVjYXRpb25fbG9hbiArIA0KICAgICMgRW1wX3N0YXR1cyArIA0KICAgICMgQW1vdW50ICsgDQogICAgU2F2aW5nX2Ftb3VudCwNCiAgICAjIEVtcF9kdXJhdGlvbiArDQogICAgIyBBZ2UgKyANCiAgICAjIE5vX29mX2NyZWRpdF9hY2MsDQogIGRhdGEgPSBsb2FuX2RlZmF1bHRfTW9kMDQNCikNCg0KIyBDb3B5aW5nIGxvYW5fZGVmYXVsdF9Nb2QwNA0KTnVtSW1wdXRlIDwtIGxvYW5fZGVmYXVsdF9Nb2QwNA0KDQojIEltcHV0aW5nIEVtcF9kdXJhdGlvbiB3aXRoIHJhbmRvbSByZWdyZXNzaW9uDQpOdW1JbXB1dGUkRW1wX2R1cmF0aW9uW2lzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwNCRFbXBfZHVyYXRpb24pXSA8LSANCiAgIyBQcmVkaWN0ZWQgdmFsdWUNCiAgcHJlZGljdCgNCiAgICBOdW1JbXB1dGVfTW9kZWxfRW1wX2R1cmF0aW9uLCANCiAgICBsb2FuX2RlZmF1bHRfTW9kMDRbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbiksXSwgDQogICAgdHlwZSA9ICJyZXNwb25zZSINCiAgKSArDQogICMgQWRkZWQgcmFuZG9tbmVzcyB2aWEgcmVzaWR1YWwNCiAgc2FtcGxlKA0KICAgIHJlc2lkKE51bUltcHV0ZV9Nb2RlbF9FbXBfZHVyYXRpb24pLCANCiAgICBzdW0oaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9kdXJhdGlvbikpLCANCiAgICByZXBsYWNlID0gVFJVRQ0KICApDQoNCiMgSW1wdXRpbmcgQWdlIHdpdGggcmFuZG9tIHJlZ3Jlc3Npb24NCk51bUltcHV0ZSRBZ2VbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEFnZSldIDwtIA0KICAjIFByZWRpY3RlZCB2YWx1ZQ0KICBwcmVkaWN0KA0KICAgIE51bUltcHV0ZV9Nb2RlbF9BZ2UsIA0KICAgIGxvYW5fZGVmYXVsdF9Nb2QwNFtpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkQWdlKSxdLCANCiAgICB0eXBlID0gInJlc3BvbnNlIg0KICApICsNCiAgIyBBZGRlZCByYW5kb21uZXNzIHZpYSByZXNpZHVhbA0KICBzYW1wbGUoDQogICAgcmVzaWQoTnVtSW1wdXRlX01vZGVsX0FnZSksIA0KICAgIHN1bShpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkQWdlKSksIA0KICAgIHJlcGxhY2UgPSBUUlVFDQogICkNCmBgYA0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgUExPVFRJTkcgRW1wX2R1cmF0aW9uIEFORCBBZ2UgV0lUSCBJTVBVVEVEIFZBTFVFUw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQpQbG90X051bUltcF9QYXJ0cyA8LSBOVUxMDQoNCiMgR2VuZXJhdGluZyBzdWJwbG90DQpQbG90X051bUltcF9QYXJ0c1tbMV1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwNCwNCiAgICB4ID0gfkVtcF9kdXJhdGlvbiwNCiAgICBuYW1lID0gIk5vbi1taXNzaW5nIiwNCiAgICBsZWdlbmRncm91cCA9ICJFbXBfZHVyYXRpb24iLA0KICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSAiRW1wX2R1cmF0aW9uIikNCiAgKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB4ID0gTnVtSW1wdXRlJEVtcF9kdXJhdGlvbltpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkRW1wX2R1cmF0aW9uKV0sDQogICAgbmFtZSA9ICJJbXB1dGVkOiBFbXBfRHVyYXRpb24iLA0KICAgIGxlZ2VuZGdyb3VwID0gIkVtcF9kdXJhdGlvbiIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJFbXBfZHVyYXRpb24iKQ0KICApDQoNCiMgR2VuZXJhdGluZyBzdWJwbG90DQpQbG90X051bUltcF9QYXJ0c1tbMl1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwNCwNCiAgICB4ID0gfkFnZSwNCiAgICBuYW1lID0gIk5vbi1taXNzaW5nIiwNCiAgICBsZWdlbmRncm91cCA9ICJBZ2UiLA0KICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSAiQWdlIikNCiAgKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB4ID0gTnVtSW1wdXRlJEFnZVtpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkQWdlKV0sDQogICAgbmFtZSA9ICJJbXB1dGVkOiBBZ2UiLA0KICAgIGxlZ2VuZGdyb3VwID0gIkFnZSIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJBZ2UiKQ0KICApDQoNCiMgR2VuZXJhdGluZyBmdWxsIHBsb3QNClBsb3RfTnVtSW1wIDwtIA0KICBzdWJwbG90KFBsb3RfTnVtSW1wX1BhcnRzW1sxXV0sIFBsb3RfTnVtSW1wX1BhcnRzW1syXV0sIG5yb3dzID0gMSwgbWFyZ2luID0gMC4wNSwgc2hhcmVZID0gVFJVRSkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiRW1wbG95bWVudCBEdXJhdGlvbiBhbmQgQWdlIHdpdGggSW1wdXRhdGlvbiIsDQogICAgYmFybW9kZSA9ICJzdGFjayIsDQogICAgYmFyZ2FwID0gMC4xLA0KICAgIGxlZ2VuZCA9IGxpc3QoDQogICAgICB0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj4gUmVzcG9uc2VzIDwvYj4iKSwNCiAgICAgIGJnY29sb3IgPSAiI0UyRTJFMiIsDQogICAgICBib3JkZXJjb2xvciA9ICIjRkZGRkZGIiwNCiAgICAgIGJvcmRlcndpZHRoID0gMg0KICAgICkNCiAgKQ0KDQojIE91dHB1dHRpbmcgcGxvdA0KUGxvdF9OdW1JbXANCmBgYA0KDQpJdCBpcyB3b3J0aCBub3RpbmcgdGhhdCB0aGlzIG1ldGhvZCBoYXMgdGhlIHBvc3NpYmlsaXR5IG9mIGltcHV0aW5nIHZhbHVlcyBvdXRzaWRlIG9mIHRoZSBwb3NzaWJsZSByYW5nZSBvZiB0aGUgZGF0YS4gVGhlcmVmb3JlLCBpdCBpcyB3b3J0aCBjb25zaWRlcmluZyBhbiBhbHRlcm5hdGl2ZSBtZXRob2Qgb2YgaW1wdXRhdGlvbi4NCg0KIyBNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKQ0KDQpNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSBpcyBhIG1ldGhvZCBvZiBpbXB1dGluZyB0aGF0IGl0ZXJhdGVzIHRocm91Z2ggZWFjaCB2YXJpYWJsZSBjb250YWluaW5nIG1pc3NpbmcgZGF0YSBhbmQgcmVmaW5pbmcgZXN0aW1hdGVzLiBUaGVyZWZvcmUsIHRoaXMgbWV0aG9kIGFsbG93cyB1cyB0byBpbXB1dGUgYEdlbmRlcmAsIGBNYXJpdGFsX3N0YXR1c2AsIGBFbXBfc3RhdHVzYCwgYEVtcF9kdXJhdGlvbmAsIGFuZCBgQWdlYCBhdCB0aGUgc2FtZSB0aW1lLg0KDQpUaGUgcmVzdWx0cyBvZiBNSUNFIGNhbiBiZSBzZWVuIGluIHRoZSBwbG90cyBiZWxvdzoNCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBFUkZPUk1JTkcgTVVMVElQTEUgSU1QVVRBVElPTg0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQpsb2FuX2RlZmF1bHRfTXVsdEltcCA8LSANCiAgY29tcGxldGUoDQogICAgbWljZSgNCiAgICAgIGxvYW5fZGVmYXVsdF9Nb2QwNFtSYXdWYXJzXSwNCiAgICAgIG1ldGhvZCA9IGMoDQogICAgICAgICMgRGVmYXVsdCANCiAgICAgICAgTkEsDQogICAgICAgICMgQ2hlY2tpbmdfYW1vdW50IA0KICAgICAgICBOQSwNCiAgICAgICAgIyBUZXJtIA0KICAgICAgICBOQSwNCiAgICAgICAgIyBDcmVkaXRfc2NvcmUgDQogICAgICAgIE5BLA0KICAgICAgICAjIEdlbmRlciANCiAgICAgICAgImxvZ3JlZyIsDQogICAgICAgICMgTWFyaXRhbF9zdGF0dXMgDQogICAgICAgICJsb2dyZWciLA0KICAgICAgICAjIENhcl9sb2FuIA0KICAgICAgICBOQSwNCiAgICAgICAgIyBQZXJzb25hbF9sb2FuIA0KICAgICAgICBOQSwNCiAgICAgICAgIyBIb21lX2xvYW4gDQogICAgICAgIE5BLA0KICAgICAgICAjIEVkdWNhdGlvbl9sb2FuIA0KICAgICAgICBOQSwNCiAgICAgICAgIyBFbXBfc3RhdHVzIA0KICAgICAgICAibG9ncmVnIiwNCiAgICAgICAgIyBBbW91bnQgDQogICAgICAgIE5BLA0KICAgICAgICAjIFNhdmluZ19hbW91bnQgDQogICAgICAgIE5BLA0KICAgICAgICAjIEVtcF9kdXJhdGlvbg0KICAgICAgICAicG1tIiwNCiAgICAgICAgIyBBZ2UgDQogICAgICAgICJwbW0iLA0KICAgICAgICAjIE5vX29mX2NyZWRpdF9hY2MNCiAgICAgICAgTkENCiAgICAgICksDQogICAgICBtYXhpdCA9IDUsDQogICAgICBwcmludCA9IEYsDQogICAgICBzZWVkID0gMTIzDQogICAgKQ0KICApDQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIENBVEVHT1JJQ0FMIFZBUklBQkxFUyBXSVRIIE1JQ0UNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KDQpNSUNFX0NhdEltcF9HZW5kZXJfQ29tYm8gPC0gZGF0YS5mcmFtZSgNCiAgQ2F0ID0gYygiRmVtYWxlIiwgIkZlbWFsZSAoSW1wdXRlZCkiLCAiTWFsZSIsICJNYWxlIChJbXB1dGVkKSIsICJOQSAoSW1wdXRlZCkiKSwNCiAgVmFsID0gYygNCiAgICB0YWJsZShsb2FuX2RlZmF1bHRfTW9kMDQkR2VuZGVyKVsiRmVtYWxlIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkR2VuZGVyW2lzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwNCRHZW5kZXIpXSwgdXNlTkEgPSAiYWx3YXlzIilbIkZlbWFsZSJdLA0KICAgIHRhYmxlKGxvYW5fZGVmYXVsdF9Nb2QwNCRHZW5kZXIpWyJNYWxlIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkR2VuZGVyW2lzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwNCRHZW5kZXIpXSwgdXNlTkEgPSAiYWx3YXlzIilbIk1hbGUiXSwNCiAgICB0YWJsZShsb2FuX2RlZmF1bHRfTXVsdEltcCRHZW5kZXJbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEdlbmRlcildLCB1c2VOQSA9ICJhbHdheXMiKVszXQ0KICApDQopDQoNCk1JQ0VfQ2F0SW1wX0dlbmRlcl9Db21ibyRDYXQgPC0gTUlDRV9DYXRJbXBfR2VuZGVyX0NvbWJvJENhdCAlPiUgDQogIGZhY3RvcihsZXZlbHMgPSBNSUNFX0NhdEltcF9HZW5kZXJfQ29tYm9bWyJDYXQiXV0pDQoNCg0KTUlDRV9DYXRJbXBfTWFyaXRhbF9zdGF0dXNfQ29tYm8gPC0gZGF0YS5mcmFtZSgNCiAgQ2F0ID0gYygiTWFycmllZCIsICJNYXJyaWVkIChJbXB1dGVkKSIsICJTaW5nbGUiLCAiU2luZ2xlIChJbXB1dGVkKSIsICJOQSAoSW1wdXRlZCkiKSwNCiAgVmFsID0gYygNCiAgICB0YWJsZShsb2FuX2RlZmF1bHRfTW9kMDQkTWFyaXRhbF9zdGF0dXMpWyJNYXJyaWVkIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkTWFyaXRhbF9zdGF0dXNbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JE1hcml0YWxfc3RhdHVzKV0sIHVzZU5BID0gImFsd2F5cyIpWyJNYXJyaWVkIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X01vZDA0JE1hcml0YWxfc3RhdHVzKVsiU2luZ2xlIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkTWFyaXRhbF9zdGF0dXNbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JE1hcml0YWxfc3RhdHVzKV0sIHVzZU5BID0gImFsd2F5cyIpWyJTaW5nbGUiXSwNCiAgICB0YWJsZShsb2FuX2RlZmF1bHRfTXVsdEltcCRNYXJpdGFsX3N0YXR1c1tpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkTWFyaXRhbF9zdGF0dXMpXSwgdXNlTkEgPSAiYWx3YXlzIilbM10NCiAgKQ0KKQ0KDQpNSUNFX0NhdEltcF9NYXJpdGFsX3N0YXR1c19Db21ibyRDYXQgPC0gTUlDRV9DYXRJbXBfTWFyaXRhbF9zdGF0dXNfQ29tYm8kQ2F0ICU+JSANCiAgZmFjdG9yKGxldmVscyA9IE1JQ0VfQ2F0SW1wX01hcml0YWxfc3RhdHVzX0NvbWJvW1siQ2F0Il1dKQ0KDQoNCk1JQ0VfQ2F0SW1wX0VtcF9zdGF0dXNfQ29tYm8gPC0gZGF0YS5mcmFtZSgNCiAgQ2F0ID0gYygiRW1wbG95ZWQiLCAiRW1wbG95ZWQgKEltcHV0ZWQpIiwgIlVuZW1wbG95ZWQiLCAiVW5lbXBsb3llZCAoSW1wdXRlZCkiLCAiTkEgKEltcHV0ZWQpIiksDQogIFZhbCA9IGMoDQogICAgdGFibGUobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9zdGF0dXMpWyJFbXBsb3llZCJdLA0KICAgIHRhYmxlKGxvYW5fZGVmYXVsdF9NdWx0SW1wJEVtcF9zdGF0dXNbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9zdGF0dXMpXSwgdXNlTkEgPSAiYWx3YXlzIilbIkVtcGxveWVkIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9zdGF0dXMpWyJVbmVtcGxveWVkIl0sDQogICAgdGFibGUobG9hbl9kZWZhdWx0X011bHRJbXAkRW1wX3N0YXR1c1tpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDQkRW1wX3N0YXR1cyldLCB1c2VOQSA9ICJhbHdheXMiKVsiVW5lbXBsb3llZCJdLA0KICAgIHRhYmxlKGxvYW5fZGVmYXVsdF9NdWx0SW1wJEVtcF9zdGF0dXNbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEVtcF9zdGF0dXMpXSwgdXNlTkEgPSAiYWx3YXlzIilbM10NCiAgKQ0KKQ0KDQpNSUNFX0NhdEltcF9FbXBfc3RhdHVzX0NvbWJvJENhdCA8LSBNSUNFX0NhdEltcF9FbXBfc3RhdHVzX0NvbWJvJENhdCAlPiUgDQogIGZhY3RvcihsZXZlbHMgPSBNSUNFX0NhdEltcF9FbXBfc3RhdHVzX0NvbWJvW1siQ2F0Il1dKQ0KDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09DQoNClBsb3RfTXVsdEltcF9DYXRfUGFydHMgPC0gTlVMTA0KDQojIEdlbmVyYXRpbmcgc3VicGxvdA0KUGxvdF9NdWx0SW1wX0NhdF9QYXJ0c1tbMV1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IE1JQ0VfQ2F0SW1wX0dlbmRlcl9Db21ibywNCiAgICB4ID0gIkdlbmRlciIsDQogICAgeSA9IH5WYWwsDQogICAgbmFtZSA9IH5DYXQsDQogICAgdHlwZSA9ICJiYXIiLA0KICAgIGxlZ2VuZGdyb3VwID0gIkdlbmRlciIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJHZW5kZXIiKQ0KICApICU+JSANCiAgbGF5b3V0KGJhcm1vZGUgPSAiZ3JvdXAiKQ0KDQojIEdlbmVyYXRpbmcgc3VicGxvdA0KUGxvdF9NdWx0SW1wX0NhdF9QYXJ0c1tbMl1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IE1JQ0VfQ2F0SW1wX01hcml0YWxfc3RhdHVzX0NvbWJvLA0KICAgIHggPSAiTWFyaXRhbF9zdGF0dXMiLA0KICAgIHkgPSB+VmFsLA0KICAgIG5hbWUgPSB+Q2F0LA0KICAgIHR5cGUgPSAiYmFyIiwNCiAgICBsZWdlbmRncm91cCA9ICJNYXJpdGFsX3N0YXR1cyIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJNYXJpdGFsX3N0YXR1cyIpDQogICkgJT4lIA0KICBsYXlvdXQoYmFybW9kZSA9ICJncm91cCIpDQoNCiMgR2VuZXJhdGluZyBzdWJwbG90DQpQbG90X011bHRJbXBfQ2F0X1BhcnRzW1szXV0gPC0gDQogIHBsb3RfbHkoKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gTUlDRV9DYXRJbXBfRW1wX3N0YXR1c19Db21ibywNCiAgICB4ID0gIkVtcF9zdGF0dXMiLA0KICAgIHkgPSB+VmFsLA0KICAgIG5hbWUgPSB+Q2F0LA0KICAgIHR5cGUgPSAiYmFyIiwNCiAgICBsZWdlbmRncm91cCA9ICJFbXBfc3RhdHVzIiwNCiAgICBsZWdlbmRncm91cHRpdGxlID0gbGlzdCh0ZXh0ID0gIkVtcF9zdGF0dXMiKQ0KICApICU+JSANCiAgbGF5b3V0KGJhcm1vZGUgPSAiZ3JvdXAiKQ0KDQoNCiMgR2VuZXJhdGluZyBmdWxsIHBsb3QNClBsb3RfTXVsdEltcF9DYXQgPC0gDQogIHN1YnBsb3QoUGxvdF9NdWx0SW1wX0NhdF9QYXJ0c1tbMV1dLCBQbG90X011bHRJbXBfQ2F0X1BhcnRzW1syXV0sIFBsb3RfTXVsdEltcF9DYXRfUGFydHNbWzNdXSwgbnJvd3MgPSAxLCBtYXJnaW4gPSAwLjA1LCBzaGFyZVkgPSBUUlVFKSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJDYXRlZ29yaWNhbCBNSUNFIFJlc3VsdHMiLA0KICAgIGJhcmdhcCA9IDAuMSwNCiAgICB5YXhpcyA9IGxpc3QoDQogICAgICB0aXRsZSA9ICIiDQogICAgKSwNCiAgICBsZWdlbmQgPSBsaXN0KA0KICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiPGI+IFJlc3BvbnNlcyA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfTXVsdEltcF9DYXQNCmBgYA0KDQpGb3IgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgTUlDRSBhcHBlYXJzIHRvIGhhdmUgcmVzdWx0ZWQgaW4gc2ltaWxhciBpbXB1dGF0aW9uIGFzIHdpdGggKmsqLU5OIGJ1dCBoYXMgcmV0dXJuZWQgbWlzc2luZyB2YWx1ZXMuIFRoZXJlZm9yZSwgTUlDRSBpbXB1dGF0aW9uIG1heSBub3QgYmUgdGhlIG9wdGltYWwgd2F5IHRvIGltcHV0ZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBOVU1FUklDQUwgVkFSSUFCTEVTIFdJVEggTUlDRQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQoNClBsb3RfTXVsdEltcF9OdW1fUGFydHMgPC0gTlVMTA0KDQojIEdlbmVyYXRpbmcgc3VicGxvdA0KUGxvdF9NdWx0SW1wX051bV9QYXJ0c1tbMV1dIDwtIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwNCwNCiAgICB4ID0gfkVtcF9kdXJhdGlvbiwNCiAgICBuYW1lID0gIk5vbi1taXNzaW5nIiwNCiAgICBsZWdlbmRncm91cCA9ICJFbXBfZHVyYXRpb24iLA0KICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSAiRW1wX2R1cmF0aW9uIikNCiAgKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB4ID0gbG9hbl9kZWZhdWx0X011bHRJbXAkRW1wX2R1cmF0aW9uW2lzLm5hKGxvYW5fZGVmYXVsdF9Nb2QwNCRFbXBfZHVyYXRpb24pXSwNCiAgICBuYW1lID0gIkltcHV0ZWQ6IEVtcF9EdXJhdGlvbiIsDQogICAgbGVnZW5kZ3JvdXAgPSAiRW1wX2R1cmF0aW9uIiwNCiAgICBsZWdlbmRncm91cHRpdGxlID0gbGlzdCh0ZXh0ID0gIkVtcF9kdXJhdGlvbiIpDQogICkgJT4lIA0KICBsYXlvdXQoYmFybW9kZSA9ICJzdGFjayIpDQoNCiMgR2VuZXJhdGluZyBzdWJwbG90DQpQbG90X011bHRJbXBfTnVtX1BhcnRzW1syXV0gPC0gDQogIHBsb3RfbHkoKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gbG9hbl9kZWZhdWx0X01vZDA0LA0KICAgIHggPSB+QWdlLA0KICAgIG5hbWUgPSAiTm9uLW1pc3NpbmciLA0KICAgIGxlZ2VuZGdyb3VwID0gIkFnZSIsDQogICAgbGVnZW5kZ3JvdXB0aXRsZSA9IGxpc3QodGV4dCA9ICJBZ2UiKQ0KICApICU+JSANCiAgYWRkX3RyYWNlKA0KICAgIHggPSBsb2FuX2RlZmF1bHRfTXVsdEltcCRBZ2VbaXMubmEobG9hbl9kZWZhdWx0X01vZDA0JEFnZSldLA0KICAgIG5hbWUgPSAiSW1wdXRlZDogQWdlIiwNCiAgICBsZWdlbmRncm91cCA9ICJBZ2UiLA0KICAgIGxlZ2VuZGdyb3VwdGl0bGUgPSBsaXN0KHRleHQgPSAiQWdlIikNCiAgKSAlPiUgDQogIGxheW91dChiYXJtb2RlID0gInN0YWNrIikNCg0KIyBHZW5lcmF0aW5nIGZ1bGwgcGxvdA0KUGxvdF9NdWx0SW1wX051bSA8LSANCiAgc3VicGxvdChQbG90X011bHRJbXBfTnVtX1BhcnRzW1sxXV0sIFBsb3RfTXVsdEltcF9OdW1fUGFydHNbWzJdXSwgbnJvd3MgPSAxLCBtYXJnaW4gPSAwLjA1LCBzaGFyZVkgPSBUUlVFKSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJOdW1lcmljYWwgTUlDRSBSZXN1bHRzIiwNCiAgICBiYXJnYXAgPSAwLjEsDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBSZXNwb25zZXMgPC9iPiIpLA0KICAgICAgYmdjb2xvciA9ICIjRTJFMkUyIiwNCiAgICAgIGJvcmRlcmNvbG9yID0gIiNGRkZGRkYiLA0KICAgICAgYm9yZGVyd2lkdGggPSAyDQogICAgKQ0KICApDQoNCiMgT3V0cHV0dGluZyBwbG90DQpQbG90X011bHRJbXBfTnVtDQpgYGANCg0KRm9yIHRoZSBudW1lcmljYWwgdmFyaWFibGVzLCBNSUNFIGFwcGVhcnMgdG8gaGF2ZSByZXN1bHRlZCBpbiBzaW1pbGFyIGltcHV0YXRpb24gYXMgd2l0aCByYW5kb20gcmVncmVzc2lvbiBidXQgZG9lcyBub3QgZ2VuZXJhdGUgdmFsdWVzIG91dHNpZGUgb2YgdGhlIHBvc3NpYmxlIHJhbmdlLiBUaGlzIGltcHJvdmVtZW50IG1ha2VzIE1JQ0UgcHJlZmVyYWJsZSB0byByYW5kb20gcmVncmVzc2lvbiBpbXB1dGF0aW9uLg0KDQoNCg==